def __init__(self, plotted_lines, *args, **kwargs): super().__init__(None, *args, **kwargs) self.plotted_lines = plotted_lines layout = QVBoxLayout() layout.setSizeConstraint(QLayout.SetMaximumSize) self.setLayout(layout) table_model = LineListTableModel(plotted_lines) if table_model.rowCount() > 0: table_view = QTableView() # disabling sorting will significantly speed up theWidget # plot. This is because the table view must be re-built # every time a new set of markers is drawn on the plot # surface. Alternate approaches are worth examining. It # remains to be seen what would be the approach users # will favor. table_view.setSortingEnabled(False) proxy = SortModel(table_model.get_name()) proxy.setSourceModel(table_model) table_view.setModel(proxy) table_view.setSortingEnabled(True) table_view.setSelectionMode(QAbstractItemView.NoSelection) table_view.horizontalHeader().setStretchLastSection(True) table_view.resizeColumnsToContents() layout.addWidget(table_view)
def __init__(self, plotted_lines, *args, **kwargs): super().__init__(None, *args, **kwargs) self.plotted_lines = plotted_lines layout = QVBoxLayout() layout.setSizeConstraint(QLayout.SetMaximumSize) self.setLayout(layout) table_model = LineListTableModel(plotted_lines) if table_model.rowCount() > 0: table_view = QTableView() # disabling sorting will significantly speed up the # plot. This is because the table view must be re-built # every time a new set of markers is drawn on the plot # surface. Alternate approaches are worth examining. It # remains to be seen what would be the approach users # will favor. table_view.setSortingEnabled(False) proxy = SortModel(table_model.getName()) proxy.setSourceModel(table_model) table_view.setModel(proxy) table_view.setSortingEnabled(True) table_view.setSelectionMode(QAbstractItemView.NoSelection) table_view.horizontalHeader().setStretchLastSection(True) table_view.resizeColumnsToContents() layout.addWidget(table_view)
def __init__(self, fileName, parent=None): QObject.__init__(self) sweepCollection = IvSweepCollection(str(fileName)) self.sweepCollection = sweepCollection tableModel = SweepTableModel(sweepCollection) tableView = QTableView() tableView.setModel(tableModel) tableView.setItemDelegateForColumn(0, CheckBoxDelegate(parent=tableView)) tableView.setItemDelegateForColumn(1, CheckBoxDelegate(parent=tableView)) self.tableModel = tableModel tableView.resizeColumnsToContents() tableView.setSelectionMode(QAbstractItemView.SingleSelection) tableView.setSelectionBehavior(QAbstractItemView.SelectRows) sm = tableView.selectionModel() sm.currentRowChanged.connect(self.selectedRowChanged) self.tableView = tableView dockWidget = QDockWidget('Sweeps') dockWidget.setWidget(tableView) self.sweepTableDock = dockWidget hkDock = HkDockWidget(sweepCollection.hk) hkDock.setWindowTitle('HK - %s' % str(fileName)) self.hkDock = hkDock ivGraphWidget = IvGraphWidget(sweepCollection) tableModel.plotToggled.connect(ivGraphWidget.showSweep) #tableModel.badToggled.connect(self.toggleBad) self.ivGraphWidget = ivGraphWidget
class DisplayShortcutsWindow(QDialog): def __init__(self, name, shortcuts): super(DisplayShortcutsWindow, self).__init__() self.name = name self.setWindowTitle(name) self.setMinimumWidth(360) self.setMinimumHeight(500) self.layout = QVBoxLayout() self.filterEntry = QLineEdit() self.filterEntry.textChanged.connect(self.filterChanged) self.layout.addWidget(self.filterEntry) self.table = QTableView() self.model = DisplayShortcutsModel(self, shortcuts) self.table.setModel(self.model) self.table.resizeColumnsToContents() self.table.setSortingEnabled(True) if self.name not in ShortcutUtil.data.keys(): self.table.doubleClicked.connect(self.clickedRow) self.layout.addWidget(self.table) if name in ShortcutUtil.data.keys(): buttons = QDialogButtonBox.Ok else: buttons = QDialogButtonBox.Ok | QDialogButtonBox.Cancel self.buttonBox = QDialogButtonBox(buttons) self.buttonBox.accepted.connect(self.saveShortcut) self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.reject) self.buttonBox.rejected.connect(self.canceled) self.layout.addWidget(self.buttonBox) self.setLayout(self.layout) def clickedRow(self, index): row = self.model.getRow(index.row()) (action, key) = row newKey, ok = QInputDialog.getText(self, 'Shortcut', action, QLineEdit.Normal, key) if ok: self.model.list[index.row()] = (action, newKey) for item in self.model.fullList: if item[0] == action: item[1] = newKey def saveShortcut(self): if self.name not in ShortcutUtil.data.keys(): ShortcutUtil.createShortcutFile(self.name, self.model.fullList) def canceled(self): if self.name not in ShortcutUtil.data.keys(): ShortcutUtil.loadShortcutFile(self.name) def filterChanged(self, text): self.model.filter(text)
class DataFrameWidget(QWidget): ''' a simple widget for using DataFrames in a gui ''' def __init__(self, dataFrame, parent=None): super(DataFrameWidget, self).__init__(parent) self.dataModel = DataFrameModel() self.dataTable = QTableView() self.dataTable.setModel(self.dataModel) layout = QVBoxLayout() layout.addWidget(self.dataTable) self.setLayout(layout) # Set DataFrame self.setDataFrame(dataFrame) def setDataFrame(self, dataFrame): self.dataModel.setDataFrame(dataFrame) self.dataModel.signalUpdate() self.dataTable.resizeColumnsToContents()
def buildViews(self, plot_window): # Table views must be preserved in the instance so they can be # passed to whoever is going to do the actual line list plotting. # The plotting code must know which lines (table rows) are selected # in each line list. self._table_views = [] for linelist in plot_window.linelists: table_model = LineListTableModel(linelist) proxy = SortModel(table_model.getName()) proxy.setSourceModel(table_model) if table_model.rowCount() > 0: table_view = QTableView() table_view.setModel(proxy) # setting this to False will significantly speed up # the loading of very large line lists. However, these # lists are often jumbled in wavelength, and consequently # difficult to read and use. It remains to be seen what # would be the approach users will favor. table_view.setSortingEnabled(True) table_view.setSelectionBehavior(QAbstractItemView.SelectRows) table_view.horizontalHeader().setStretchLastSection(True) table_view.resizeColumnsToContents() comments = linelist.meta['comments'] # this preserves the original sorting state # of the list. Use zero to sort by wavelength # on load. Doesn't seem to affect performance # by much tough. proxy.sort(-1, Qt.AscendingOrder) pane = self._buildLinelistPane(table_view, comments) self.tabWidget.addTab(pane, table_model.getName()) self._table_views.append(table_view)
def _create_line_list_pane(linelist, table_model, caller): table_view = QTableView() # disabling sorting will significantly speed up the rendering, # in particular of large line lists. These lists are often jumbled # in wavelength, and consequently difficult to read and use, so # having a sorting option is useful indeed. It remains to be seen # what would be the approach users will favor. We might add a toggle # that users can set/reset depending on their preferences. table_view.setSortingEnabled(False) sort_proxy = SortModel(table_model.get_name()) sort_proxy.setSourceModel(table_model) table_view.setModel(sort_proxy) table_view.setSortingEnabled(True) table_view.horizontalHeader().setStretchLastSection(True) # playing with these doesn't speed up the sorting, regardless of whatever # you may read on the net. # # table_view.horizontalHeader().setResizeMode(QHeaderView.Fixed) # table_view.verticalHeader().setResizeMode(QHeaderView.Fixed) # table_view.horizontalHeader().setStretchLastSection(False) # table_view.verticalHeader().setStretchLastSection(False) table_view.setSelectionMode(QAbstractItemView.ExtendedSelection) table_view.setSelectionBehavior(QAbstractItemView.SelectRows) table_view.resizeColumnsToContents() # this preserves the original sorting state of the list. Use zero # to sort by wavelength on load. Doesn't seem to affect performance # by much tough. sort_proxy.sort(-1, Qt.AscendingOrder) # table selections will change the total count of lines selected. pane = LineListPane(table_view, linelist, sort_proxy, caller) return pane, table_view
def _createLineListPane(linelist, table_model, caller): table_view = QTableView() # disabling sorting will significantly speed up the rendering, # in particular of large line lists. These lists are often jumbled # in wavelength, and consequently difficult to read and use, so # having a sorting option is useful indeed. It remains to be seen # what would be the approach users will favor. We might add a toggle # that users can set/reset depending on their preferences. table_view.setSortingEnabled(False) sort_proxy = SortModel(table_model.getName()) sort_proxy.setSourceModel(table_model) table_view.setModel(sort_proxy) table_view.setSortingEnabled(True) table_view.horizontalHeader().setStretchLastSection(True) # playing with these doesn't speed up the sorting, regardless of whatever # you may read on the net. # # table_view.horizontalHeader().setResizeMode(QHeaderView.Fixed) # table_view.verticalHeader().setResizeMode(QHeaderView.Fixed) # table_view.horizontalHeader().setStretchLastSection(False) # table_view.verticalHeader().setStretchLastSection(False) table_view.setSelectionMode(QAbstractItemView.ExtendedSelection) table_view.setSelectionBehavior(QAbstractItemView.SelectRows) table_view.resizeColumnsToContents() # this preserves the original sorting state of the list. Use zero # to sort by wavelength on load. Doesn't seem to affect performance # by much tough. sort_proxy.sort(-1, Qt.AscendingOrder) # table selections will change the total count of lines selected. pane = LineListPane(table_view, linelist, sort_proxy, caller) return pane, table_view
class ConfigurationManager(SiriusMainWindow): """.""" NAME_COL = None CONFIG_TYPE_COL = None def __init__(self, model, parent=None): """Constructor.""" super().__init__(parent) self._model = model self._logger = logging.getLogger(__name__) self._logger.setLevel(logging.INFO) self._setup_ui() self.setWindowTitle("Configuration Manager") def _setup_ui(self): # self.setGeometry(0, 0, 1600, 900) self.main_widget = QFrame() self.main_widget.setObjectName('ServConf') self.setCentralWidget(self.main_widget) self.layout = QGridLayout() self.main_widget.setLayout(self.layout) # Basic widgets self.editor = QTableView() self.delete_button = QPushButton('Delete', self) self.delete_button.setObjectName('DeleteButton') self.rename_button = QPushButton('Rename', self) self.rename_button.setObjectName('RenameButton') self.d_editor = QTableView() self.retrieve_button = QPushButton('Retrieve', self) self.retrieve_button.setObjectName('RetrieveButton') self.tree = QTreeView(self) self.config_type = QComboBox(parent=self) self.config_type.setModel( ConfigTypeModel(self._model, self.config_type)) # Tab widgets self.tab1 = QWidget() self.tab1.layout = QVBoxLayout(self.tab1) self.tab2 = QWidget() self.tab2.layout = QVBoxLayout(self.tab2) self.tab1.layout.addWidget(self.editor) hlay = QHBoxLayout() hlay.addWidget(self.rename_button) hlay.addWidget(self.delete_button) self.tab1.layout.addLayout(hlay) self.tab2.layout.addWidget(self.d_editor) self.tab2.layout.addWidget(self.retrieve_button) self.editor_tab = QTabWidget(self) self.editor_tab.addTab(self.tab1, 'Configurations') self.editor_tab.addTab(self.tab2, 'Discarded Configurations') self.config_viewer = QWidget(self) self.config_viewer.layout = QVBoxLayout(self.config_viewer) self.config_viewer.layout.addWidget(self.editor_tab) self.config_viewer.layout.addWidget(self.tree) # Header widget self.header = QFrame(self) self.header.setObjectName('Header') self.header.layout = QHBoxLayout(self.header) self.header.layout.addStretch() self.header.layout.addWidget( QLabel('Configuration Database Manager', self.header)) self.header.layout.addStretch() # Sub header with database genral information self.sub_header = QFrame(self) self.sub_header.setObjectName('SubHeader') self.sub_header.layout = QVBoxLayout(self.sub_header) self.server_layout = QHBoxLayout() self.server_layout.addWidget(QLabel('<b>Server:</b>', self.sub_header)) self.server_layout.addWidget(QLabel(self._model.url, self.sub_header)) self.server_layout.addStretch() self.size_layout = QHBoxLayout() self.size_layout.addWidget(QLabel('<b>DB Size:</b>', self.sub_header)) try: dbsize = self._model.get_dbsize() dbsize = '{:.2f} MB'.format(dbsize/(1024*1024)) except ConfigDBException: dbsize = 'Failed to retrieve information' self.size_layout.addWidget(QLabel(dbsize, self.sub_header)) self.size_layout.addStretch() self.sub_header.layout.addLayout(self.server_layout) self.sub_header.layout.addLayout(self.size_layout) # Query form self.query_form = QFrame() self.query_form.setObjectName("QueryForm") self.query_form.layout = QVBoxLayout() self.query_form.setLayout(self.query_form.layout) self.configs_layout = QGridLayout() self.configs_layout.addWidget(QLabel('Configurations:', self), 0, 0) self.nr_configs = QLabel(self) self.configs_layout.addWidget(self.nr_configs, 0, 1) self.configs_layout.addWidget(QLabel('Discarded:', self), 0, 2) self.nr_discarded = QLabel(self) self.configs_layout.addWidget(self.nr_discarded, 0, 3) self.query_form.layout.addWidget(self.config_type) self.query_form.layout.addLayout(self.configs_layout) # Main widget layout setup self.layout.addWidget(self.header, 0, 0, 1, 3) self.layout.addWidget(self.sub_header, 1, 0, 1, 2) self.layout.addWidget(self.query_form, 2, 0, 1, 2) self.layout.addWidget(self.config_viewer, 3, 0, 1, 2) self.layout.addWidget(self.tree, 1, 2, 4, 1) # self.layout.addWidget(self.delete_button, 4, 0, 1, 2) self.layout.setColumnStretch(0, 1) self.layout.setColumnStretch(1, 2) self.layout.setColumnStretch(2, 2) # Set table models and options self.editor_model = ConfigDbTableModel('notexist', self._model) self.d_editor_model = ConfigDbTableModel('notexist', self._model, True) self.editor.setModel(self.editor_model) self.editor.setSelectionBehavior(self.editor.SelectRows) self.editor.setSortingEnabled(True) self.editor.horizontalHeader().setResizeMode(QHeaderView.Stretch) self.d_editor.setModel(self.d_editor_model) self.d_editor.setSelectionBehavior(self.editor.SelectRows) self.d_editor.setSortingEnabled(True) self.d_editor.horizontalHeader().setResizeMode(QHeaderView.Stretch) self.d_editor.setSelectionMode(self.d_editor.SingleSelection) # Set tree model and options self.tree_model = JsonTreeModel(None, None, self._model) self.tree.setModel(self.tree_model) # Delete button self.delete_button.setEnabled(False) self.rename_button.setEnabled(True) self.retrieve_button.setEnabled(False) # Signals and slots # Tab self.editor_tab.currentChanged.connect(self._tab_changed) # Fill tables when configuration is selected self.config_type.currentTextChanged.connect(self._fill_table) # Fill tree when a configuration is selected self.editor.selectionModel().selectionChanged.connect( lambda x, y: self._fill_tree()) self.d_editor.selectionModel().selectionChanged.connect( lambda x, y: self._fill_tree()) # Connect database error to slot that show messages self.editor_model.connectionError.connect(self._database_error) self.d_editor_model.connectionError.connect(self._database_error) # Makes tree column extend automatically to show content self.tree.expanded.connect( lambda idx: self.tree.resizeColumnToContents(idx.column())) # Button action self.delete_button.pressed.connect(self._remove_configuration) self.rename_button.pressed.connect(self._rename_configuration) self.retrieve_button.pressed.connect(self._retrieve_configuration) # Set constants ConfigurationManager.NAME_COL = \ self.editor_model.horizontalHeader.index('name') ConfigurationManager.CONFIG_TYPE_COL = \ self.editor_model.horizontalHeader.index('config_type') self.editor.resizeColumnsToContents() self.d_editor.resizeColumnsToContents() @Slot(str) def _fill_table(self, config_type): """Fill table with configuration of `config_type`.""" leng = len(self._model.find_configs( config_type=config_type, discarded=False)) self.nr_configs.setText(str(leng)) leng = len(self._model.find_configs( config_type=config_type, discarded=True)) self.nr_discarded.setText(str(leng)) self.editor_model.setupModelData(config_type) self.d_editor_model.setupModelData(config_type) self.editor.resizeColumnsToContents() self.d_editor.resizeColumnsToContents() self.editor_model.sort(2, Qt.DescendingOrder) self.d_editor_model.sort(2, Qt.DescendingOrder) @Slot() def _fill_tree(self): if self.editor_tab.currentIndex() == 0: configs = list() rows = self._get_selected_rows(self.editor) # Get selected rows for row in rows: # Get name and configuration type configs.append(self._type_name(row, self.editor_model)) # Set tree data self.tree_model.setupModelData(configs) if len(configs) == 1: self.delete_button.setEnabled(True) self.delete_button.setText( 'Delete {} ({})'.format(configs[0][1], configs[0][0])) self.rename_button.setEnabled(True) self.rename_button.setText( 'Rename {} ({})'.format(configs[0][1], configs[0][0])) elif len(configs) > 1: self.rename_button.setEnabled(False) self.rename_button.setText('Rename') self.delete_button.setEnabled(True) self.delete_button.setText( 'Delete {} configurations'.format(len(configs))) else: self.rename_button.setEnabled(False) self.rename_button.setText('Rename') self.delete_button.setEnabled(False) self.delete_button.style().polish(self.delete_button) self.rename_button.style().polish(self.rename_button) else: try: row = self._get_selected_rows(self.d_editor).pop() except KeyError: self.retrieve_button.setEnabled(False) self.retrieve_button.style().polish(self.retrieve_button) else: config_type, name = self._type_name(row, self.d_editor_model) self.tree_model.setupModelData([(config_type, name)]) self.retrieve_button.setEnabled(True) self.retrieve_button.style().polish(self.retrieve_button) # self.tree.resizeColumnsToContents() @Slot() def _remove_configuration(self): type = QMessageBox.Question title = 'Remove configuration?' buttons = QMessageBox.Ok | QMessageBox.Cancel # self.editor.selectRow(index.row()) rows = list(self._get_selected_rows(self.editor)) message = 'Remove configurations:\n' for row in rows: config_type = self.editor_model.createIndex(row, 0).data() name = self.editor_model.createIndex(row, 1).data() message += '- {} ({})\n'.format(name, config_type) msg = QMessageBox(type, title, message, buttons).exec_() if msg == QMessageBox.Ok: rows.sort(reverse=True) for row in rows: self.editor_model.removeRows(row) self.editor.selectionModel().clearSelection() self._fill_table(self.config_type.currentText()) @Slot() def _rename_configuration(self): # self.editor.selectRow(index.row()) rows = list(self._get_selected_rows(self.editor)) if not rows: return config_type = self.editor_model.createIndex(rows[0], 0).data() name = self.editor_model.createIndex(rows[0], 1).data() wid = RenameConfigDialog(config_type, self) wid.setWindowTitle('Rename: {}'.format(name)) wid.search_le.setText(name) newname, status = wid.exec_() if not newname or not status: return self._model.rename_config( name, newname, config_type=config_type) self.editor.selectionModel().clearSelection() self._fill_table(self.config_type.currentText()) @Slot() def _retrieve_configuration(self): type = QMessageBox.Question title = 'Retrieve configuration?' buttons = QMessageBox.Ok | QMessageBox.Cancel try: row = self._get_selected_rows(self.d_editor).pop() except KeyError: pass else: config_type, name = self._type_name(row, self.d_editor_model) name = name[:-37] message = \ 'Retrieve configuration {} ({})?'.format(config_type, name) msg = QMessageBox(type, title, message, buttons).exec_() if msg == QMessageBox.Ok: try: self.d_editor_model.removeRows(row) except TypeError: self._database_error( 'Exception', 'Configuration no longer is in the correct format', 'retrieve configuration') self.editor.selectionModel().clearSelection() self._fill_table(self.config_type.currentText()) @Slot(int) def _tab_changed(self, index): if index == 0: self.editor.selectionModel().clearSelection() self.delete_button.setText('Delete') self.delete_button.setEnabled(False) self.delete_button.style().polish(self.delete_button) self.rename_button.setText('Rename') self.rename_button.setEnabled(False) self.rename_button.style().polish(self.rename_button) else: self.d_editor.selectionModel().clearSelection() self.retrieve_button.setEnabled(False) self.retrieve_button.style().polish(self.retrieve_button) self.tree_model.setupModelData([]) @Slot(int, str, str) def _database_error(self, code, message, operation): type = QMessageBox.Warning title = 'Something went wrong' msg = '{}: {}, while trying to {}'.format(code, message, operation) QMessageBox(type, title, msg).exec_() def _get_selected_rows(self, table): index_list = table.selectionModel().selectedIndexes() return {idx.row() for idx in index_list} def _type_name(self, row, model): # Return config_type and name given a row and a table model return (model.createIndex(row, self.CONFIG_TYPE_COL).data(), model.createIndex(row, self.NAME_COL).data())
class MxDataWidget(QWidget): """ Dialog for displaying and editing DataFrame and related objects. Based on the gtabview project (ExtTableView). For more information please see: https://github.com/wavexx/gtabview/blob/master/gtabview/viewer.py Signals ------- sig_option_changed(str, object): Raised if an option is changed. Arguments are name of option and its new value. """ sig_option_changed = Signal(str, object) def __init__(self, parent=None, data=DataFrame()): QWidget.__init__(self, parent) # Destroying the C++ object right after closing the dialog box, # otherwise it may be garbage-collected in another QThread # (e.g. the editor's analysis thread in Spyder), thus leading to # a segmentation fault on UNIX or an application crash on Windows self.setAttribute(Qt.WA_DeleteOnClose) self.is_series = False self.layout = None self.setup_and_check(data) def setup_and_check(self, data, title=''): """ Setup DataFrameEditor: return False if data is not supported, True otherwise. Supported types for data are DataFrame, Series and Index. """ self._selection_rec = False self._model = None self.layout = QGridLayout() self.layout.setSpacing(0) self.layout.setContentsMargins(0, 0, 0, 0) self.setLayout(self.layout) self.setWindowIcon(ima.icon('arredit')) if title: title = to_text_string(title) + " - %s" % data.__class__.__name__ else: title = _("%s editor") % data.__class__.__name__ if isinstance(data, Series): self.is_series = True data = data.to_frame() elif isinstance(data, Index): data = DataFrame(data) self.setWindowTitle(title) # self.resize(600, 500) self.hscroll = QScrollBar(Qt.Horizontal) self.vscroll = QScrollBar(Qt.Vertical) # Create the view for the level self.create_table_level() # Create the view for the horizontal header self.create_table_header() # Create the view for the vertical index self.create_table_index() # Create the model and view of the data self.dataModel = MxDataModel(data, parent=self) # self.dataModel.dataChanged.connect(self.save_and_close_enable) self.create_data_table() self.layout.addWidget(self.hscroll, 2, 0, 1, 2) self.layout.addWidget(self.vscroll, 0, 2, 2, 1) # autosize columns on-demand self._autosized_cols = set() self._max_autosize_ms = None self.dataTable.installEventFilter(self) avg_width = self.fontMetrics().averageCharWidth() self.min_trunc = avg_width * 8 # Minimum size for columns self.max_width = avg_width * 64 # Maximum size for columns self.setLayout(self.layout) # Make the dialog act as a window # self.setWindowFlags(Qt.Window) self.setModel(self.dataModel) self.resizeColumnsToContents() return True def create_table_level(self): """Create the QTableView that will hold the level model.""" self.table_level = QTableView() self.table_level.setEditTriggers(QTableWidget.NoEditTriggers) self.table_level.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.table_level.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.table_level.setFrameStyle(QFrame.Plain) self.table_level.horizontalHeader().sectionResized.connect( self._index_resized) self.table_level.verticalHeader().sectionResized.connect( self._header_resized) # self.table_level.setItemDelegate(QItemDelegate()) self.layout.addWidget(self.table_level, 0, 0) self.table_level.setContentsMargins(0, 0, 0, 0) self.table_level.horizontalHeader().sectionClicked.connect( self.sortByIndex) def create_table_header(self): """Create the QTableView that will hold the header model.""" self.table_header = QTableView() self.table_header.verticalHeader().hide() self.table_header.setEditTriggers(QTableWidget.NoEditTriggers) self.table_header.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.table_header.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.table_header.setHorizontalScrollMode(QTableView.ScrollPerPixel) self.table_header.setHorizontalScrollBar(self.hscroll) self.table_header.setFrameStyle(QFrame.Plain) self.table_header.horizontalHeader().sectionResized.connect( self._column_resized) # self.table_header.setItemDelegate(QItemDelegate()) self.layout.addWidget(self.table_header, 0, 1) def create_table_index(self): """Create the QTableView that will hold the index model.""" self.table_index = QTableView() self.table_index.horizontalHeader().hide() self.table_index.setEditTriggers(QTableWidget.NoEditTriggers) self.table_index.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.table_index.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.table_index.setVerticalScrollMode(QTableView.ScrollPerPixel) self.table_index.setVerticalScrollBar(self.vscroll) self.table_index.setFrameStyle(QFrame.Plain) self.table_index.verticalHeader().sectionResized.connect( self._row_resized) # self.table_index.setItemDelegate(QItemDelegate()) self.layout.addWidget(self.table_index, 1, 0) self.table_index.setContentsMargins(0, 0, 0, 0) def create_data_table(self): """Create the QTableView that will hold the data model.""" self.dataTable = MxDataTable(self, self.dataModel, self.table_header.horizontalHeader(), self.hscroll, self.vscroll) self.dataTable.verticalHeader().hide() self.dataTable.horizontalHeader().hide() self.dataTable.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.dataTable.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.dataTable.setHorizontalScrollMode(QTableView.ScrollPerPixel) self.dataTable.setVerticalScrollMode(QTableView.ScrollPerPixel) self.dataTable.setFrameStyle(QFrame.Plain) # self.dataTable.setItemDelegate(QItemDelegate()) self.layout.addWidget(self.dataTable, 1, 1) self.setFocusProxy(self.dataTable) self.dataTable.sig_sort_by_column.connect(self._sort_update) self.dataTable.sig_fetch_more_columns.connect(self._fetch_more_columns) self.dataTable.sig_fetch_more_rows.connect(self._fetch_more_rows) def sortByIndex(self, index): """Implement a Index sort.""" self.table_level.horizontalHeader().setSortIndicatorShown(True) sort_order = self.table_level.horizontalHeader().sortIndicatorOrder() self.table_index.model().sort(index, sort_order) self._sort_update() def model(self): """Get the model of the dataframe.""" return self._model def _column_resized(self, col, old_width, new_width): """Update the column width.""" self.dataTable.setColumnWidth(col, new_width) self._update_layout() def _row_resized(self, row, old_height, new_height): """Update the row height.""" self.dataTable.setRowHeight(row, new_height) self._update_layout() def _index_resized(self, col, old_width, new_width): """Resize the corresponding column of the index section selected.""" self.table_index.setColumnWidth(col, new_width) self._update_layout() def _header_resized(self, row, old_height, new_height): """Resize the corresponding row of the header section selected.""" self.table_header.setRowHeight(row, new_height) self._update_layout() def _update_layout(self): """Set the width and height of the QTableViews and hide rows.""" h_width = max(self.table_level.verticalHeader().sizeHint().width(), self.table_index.verticalHeader().sizeHint().width()) self.table_level.verticalHeader().setFixedWidth(h_width) self.table_index.verticalHeader().setFixedWidth(h_width) last_row = self._model.header_shape[0] - 1 if last_row < 0: hdr_height = self.table_level.horizontalHeader().height() else: # Check if the header shape has only one row (which display the # same info than the horizontal header). if last_row == 0: self.table_level.setRowHidden(0, True) self.table_header.setRowHidden(0, True) else: self.table_level.setRowHidden(0, False) self.table_header.setRowHidden(0, False) hdr_height = self.table_level.rowViewportPosition(last_row) + \ self.table_level.rowHeight(last_row) + \ self.table_level.horizontalHeader().height() self.table_header.setFixedHeight(hdr_height) self.table_level.setFixedHeight(hdr_height) last_col = self._model.header_shape[1] - 1 if last_col < 0: idx_width = self.table_level.verticalHeader().width() else: idx_width = self.table_level.columnViewportPosition(last_col) + \ self.table_level.columnWidth(last_col) + \ self.table_level.verticalHeader().width() self.table_index.setFixedWidth(idx_width) self.table_level.setFixedWidth(idx_width) self._resizeVisibleColumnsToContents() def _reset_model(self, table, model): """Set the model in the given table.""" old_sel_model = table.selectionModel() table.setModel(model) if old_sel_model: del old_sel_model def setAutosizeLimit(self, limit_ms): """Set maximum size for columns.""" self._max_autosize_ms = limit_ms def setModel(self, model, relayout=True): """Set the model for the data, header/index and level views.""" self._model = model # sel_model = self.dataTable.selectionModel() # sel_model.currentColumnChanged.connect( # self._resizeCurrentColumnToContents) self._reset_model(self.dataTable, model) # Asociate the models (level, vertical index and horizontal header) # with its corresponding view. self._reset_model( self.table_level, DataFrameLevelModel(model, self.palette(), self.font())) self._reset_model(self.table_header, DataFrameHeaderModel(model, 0, self.palette())) self._reset_model(self.table_index, DataFrameHeaderModel(model, 1, self.palette())) # Needs to be called after setting all table models if relayout: self._update_layout() def setCurrentIndex(self, y, x): """Set current selection.""" self.dataTable.selectionModel().setCurrentIndex( self.dataTable.model().index(y, x), QItemSelectionModel.ClearAndSelect) def _sizeHintForColumn(self, table, col, limit_ms=None): """Get the size hint for a given column in a table.""" max_row = table.model().rowCount() lm_start = time.perf_counter() lm_row = 64 if limit_ms else max_row max_width = 0 for row in range(max_row): v = table.sizeHintForIndex(table.model().index(row, col)) max_width = max(max_width, v.width()) if row > lm_row: lm_now = time.perf_counter() lm_elapsed = (lm_now - lm_start) * 1000 if lm_elapsed >= limit_ms: break lm_row = int((row / lm_elapsed) * limit_ms) return max_width def _resizeColumnToContents(self, header, data, col, limit_ms): """Resize a column by its contents.""" hdr_width = self._sizeHintForColumn(header, col, limit_ms) data_width = self._sizeHintForColumn(data, col, limit_ms) if data_width > hdr_width: width = min(self.max_width, data_width) elif hdr_width > data_width * 2: width = max(min(hdr_width, self.min_trunc), min(self.max_width, data_width)) else: width = min(self.max_width, hdr_width) header.setColumnWidth(col, width) def _resizeColumnsToContents(self, header, data, limit_ms): """Resize all the colummns to its contents.""" max_col = data.model().columnCount() if limit_ms is None: max_col_ms = None else: max_col_ms = limit_ms / max(1, max_col) for col in range(max_col): self._resizeColumnToContents(header, data, col, max_col_ms) def eventFilter(self, obj, event): """Override eventFilter to catch resize event.""" if obj == self.dataTable and event.type() == QEvent.Resize: self._resizeVisibleColumnsToContents() return False def _resizeVisibleColumnsToContents(self): """Resize the columns that are in the view.""" index_column = self.dataTable.rect().topLeft().x() start = col = self.dataTable.columnAt(index_column) width = self._model.shape[1] end = self.dataTable.columnAt(self.dataTable.rect().bottomRight().x()) end = width if end == -1 else end + 1 if self._max_autosize_ms is None: max_col_ms = None else: max_col_ms = self._max_autosize_ms / max(1, end - start) while col < end: resized = False if col not in self._autosized_cols: self._autosized_cols.add(col) resized = True self._resizeColumnToContents(self.table_header, self.dataTable, col, max_col_ms) col += 1 if resized: # As we resize columns, the boundary will change index_column = self.dataTable.rect().bottomRight().x() end = self.dataTable.columnAt(index_column) end = width if end == -1 else end + 1 if max_col_ms is not None: max_col_ms = self._max_autosize_ms / max(1, end - start) def _resizeCurrentColumnToContents(self, new_index, old_index): """Resize the current column to its contents.""" if new_index.column() not in self._autosized_cols: # Ensure the requested column is fully into view after resizing self._resizeVisibleColumnsToContents() self.dataTable.scrollTo(new_index) def resizeColumnsToContents(self): """Resize the columns to its contents.""" self._autosized_cols = set() self._resizeColumnsToContents(self.table_level, self.table_index, self._max_autosize_ms) self._update_layout() self.table_level.resizeColumnsToContents() def change_format(self): """ Ask user for display format for floats and use it. This function also checks whether the format is valid and emits `sig_option_changed`. """ format, valid = QInputDialog.getText(self, _('Format'), _("Float formatting"), QLineEdit.Normal, self.dataModel.get_format()) if valid: format = str(format) try: format % 1.1 except: msg = _("Format ({}) is incorrect").format(format) QMessageBox.critical(self, _("Error"), msg) return if not format.startswith('%'): msg = _("Format ({}) should start with '%'").format(format) QMessageBox.critical(self, _("Error"), msg) return self.dataModel.set_format(format) self.sig_option_changed.emit('dataframe_format', format) def get_value(self): """Return modified Dataframe -- this is *not* a copy""" # It is import to avoid accessing Qt C++ object as it has probably # already been destroyed, due to the Qt.WA_DeleteOnClose attribute df = self.dataModel.get_data() if self.is_series: return df.iloc[:, 0] else: return df def _update_header_size(self): """Update the column width of the header.""" column_count = self.table_header.model().columnCount() for index in range(0, column_count): if index < column_count: column_width = self.dataTable.columnWidth(index) self.table_header.setColumnWidth(index, column_width) else: break def _sort_update(self): """ Update the model for all the QTableView objects. Uses the model of the dataTable as the base. """ self.setModel(self.dataTable.model()) def _fetch_more_columns(self): """Fetch more data for the header (columns).""" self.table_header.model().fetch_more() def _fetch_more_rows(self): """Fetch more data for the index (rows).""" self.table_index.model().fetch_more() def resize_to_contents(self): QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) self.dataTable.resizeColumnsToContents() self.dataModel.fetch_more(columns=True) self.dataTable.resizeColumnsToContents() self._update_header_size() QApplication.restoreOverrideCursor() # --- mx specific --- def process_remote_view(self, data): if data is None: data = DataFrame() # Empty DataFrame self.setModel(MxDataModel(data, parent=self))
class InterlinearDataWindow(QWidget): translation = ( "Interlinear Data", "Enter a reference:", "No bible verse reference is found!", "Export to Spreadsheet", "Close", ) def __init__(self, parent, initialVerse=""): super().__init__() self.parent = parent # set title self.setWindowTitle(self.translation[0]) self.setMinimumSize(830, 500) # set variables self.setupVariables() # setup interface self.setupUI(initialVerse) def setupVariables(self): import os, sqlite3 # connect bibles.sqlite self.database = os.path.join(config.marvelData, "morphology.sqlite") self.connection = sqlite3.connect(self.database) self.cursor = self.connection.cursor() # Spreadsheet headers self.headers = ("WordID", "ClauseID", "Book", "Chapter", "Verse", "Word", "LexicalEntry", "MorphologyCode", "Morphology", "Lexeme", "Transliteration", "Pronunciation", "Interlinear", "Translation", "Gloss") def __del__(self): self.connection.close() def setupUI(self, initialVerse=""): from qtpy.QtGui import QStandardItemModel from qtpy.QtWidgets import (QPushButton, QLabel, QTableView, QHBoxLayout, QVBoxLayout, QLineEdit) #from qtpy.QtWidgets import QAbstractItemView mainLayout = QVBoxLayout() mainLayout.addWidget(QLabel(self.translation[0])) layout = QHBoxLayout() layout.addWidget(QLabel(self.translation[1])) self.searchEntry = QLineEdit() # Set initial entry self.searchEntry.setText(initialVerse if initialVerse else "John 3:16") self.searchEntry.returnPressed.connect(self.resetItems) layout.addWidget(self.searchEntry) mainLayout.addLayout(layout) self.dataView = QTableView() # Allow editing so that users can select text and copy #self.dataView.setEditTriggers(QAbstractItemView.NoEditTriggers) self.dataView.setSortingEnabled(True) self.dataViewModel = QStandardItemModel(self.dataView) self.dataView.setModel(self.dataViewModel) self.resetItems() mainLayout.addWidget(self.dataView) buttonLayout = QHBoxLayout() button = QPushButton(self.translation[4]) button.clicked.connect(self.close) buttonLayout.addWidget(button) button = QPushButton(self.translation[3]) button.clicked.connect(self.exportSpreadsheet) buttonLayout.addWidget(button) mainLayout.addLayout(buttonLayout) self.setLayout(mainLayout) def resetItems(self): from util.BibleVerseParser import BibleVerseParser from qtpy.QtGui import QStandardItem # Empty the model before reset self.dataViewModel.clear() # Reset # Parse entered reference reference = self.searchEntry.text().strip() verses = BibleVerseParser( config.parserStandarisation).extractAllReferences( reference, False) if verses: # Search morphology database bcv = verses[0][0:3] query = "SELECT * FROM morphology WHERE Book = ? AND Chapter = ? AND Verse = ?" self.cursor.execute(query, bcv) self.results = self.cursor.fetchall() # Display data # TABLE morphology (WordID INT, ClauseID INT, Book INT, Chapter INT, Verse INT, Word TEXT, LexicalEntry TEXT, MorphologyCode TEXT, Morphology TEXT, Lexeme TEXT, Transliteration TEXT, Pronunciation TEXT, Interlinear TEXT, Translation TEXT, Gloss TEXT) #for wordID, clauseID, b, c, v, textWord, lexicalEntry, morphologyCode, morphology, lexeme, transliteration, pronuciation, interlinear, translation, gloss in self.results: for row, result in enumerate(self.results): for column in range(0, len(result)): text = str( result[column]) if column < 5 else result[column] item = QStandardItem(text) self.dataViewModel.setItem(row, column, item) self.dataViewModel.setHorizontalHeaderLabels([ "WordID", "ClauseID", "Book", "Chapter", "Verse", "Word", "LexicalEntry", "MorphologyCode", "Morphology", "Lexeme", "Transliteration", "Pronunciation", "Interlinear", "Translation", "Gloss" ]) else: self.results = [] self.displayMessage(self.translation[2]) self.dataView.resizeColumnsToContents() def displayMessage(self, message): from qtpy.QtWidgets import QMessageBox QMessageBox.information(self, self.translation[0], message) def exportSpreadsheet(self): import sys from install.module import installmodule module = "openpyxl" # Check if essential module is installed try: from openpyxl import Workbook moduleInstalled = True except: moduleInstalled = False # Install essential module if it is absent if not moduleInstalled: installmodule(module) if not module in sys.modules: try: from openpyxl import Workbook self.runExportSpreadsheet() except: #self.displayMessage("Package '{0}' is required but not installed!\nRun 'pip3 install {0}' to install it first.".format(module)) # openpyxl requires pyton version 3.6+, try alternative 'xlsxwriter' self.exportSpreadsheet2() else: self.runExportSpreadsheet() def runExportSpreadsheet(self): from openpyxl import Workbook from openpyxl.styles import Font # Specify excel file path #filePath = os.path.join(os.getcwd(), "plugins", "menu", "Interlinear_Data.xlsx") filePath = self.getFilePath() if filePath: # Documentation on openpyxl: https://openpyxl.readthedocs.io/en/stable/ wb = Workbook() # grab the active worksheet ws = wb.active ws.title = "UniqueBible.app" # Append rows ws.append(self.headers) font = Font(bold=True) for i in range(0, len(self.headers)): # row and column number starts from 1 when calling ws.cell ws.cell(row=1, column=i + 1).font = font if self.results: for result in self.results: ws.append(result) # Apply style # Documentation: https://openpyxl.readthedocs.io/en/stable/styles.html font = Font( name="Calibri") if self.results[0][2] >= 40 else Font( name="Ezra SIL") for column in ("F", "J"): for row in range(0, len(self.results)): ws["{0}{1}".format(column, row + 2)].font = font font = Font(name="Calibri") for column in ("K", "L"): for row in range(0, len(self.results)): ws["{0}{1}".format(column, row + 2)].font = font # Save and open the file wb.save(filePath) self.openFile(filePath) # Use 'xlsxwriter' to export excel file if 'openpyxl' is not installed. def exportSpreadsheet2(self): import sys from install.module import installmodule module = "xlsxwriter" # Check if essential module is installed try: import xlsxwriter moduleInstalled = True except: moduleInstalled = False # Install essential module if it is absent if not moduleInstalled: installmodule(module) if not module in sys.modules: try: import xlsxwriter self.runExportSpreadsheet2() except: #self.displayMessage("Package '{0}' is required but not installed!\nRun 'pip3 install {0}' to install it first.".format(module)) self.exportSpreadsheet3() else: self.runExportSpreadsheet2() def runExportSpreadsheet2(self): import xlsxwriter # Specify excel file path #filePath = os.path.join(os.getcwd(), "plugins", "menu", "Interlinear_Data.xlsx") filePath = self.getFilePath() if filePath: # Create an new Excel file and add a worksheet. # Documentation on xlsxwriter: https://pypi.org/project/XlsxWriter/ workbook = xlsxwriter.Workbook(filePath) worksheet = workbook.add_worksheet("UniqueBible.app") # Add formats to cells. bold = workbook.add_format({'bold': True}) format_right_to_left = workbook.add_format({'reading_order': 2}) # Text with formatting. for index, header in enumerate(self.headers): worksheet.write(0, index, header, bold) if self.results: for row, result in enumerate(self.results): for column, item in enumerate(result): if column in (5, 9) and self.results[0][2] < 40: worksheet.write(row + 1, column, item, format_right_to_left) else: worksheet.write(row + 1, column, item) workbook.close() # Open the saved file self.openFile(filePath) # export to csv when users cannot install either openpyxl or xlsxwriter for some reasons def exportSpreadsheet3(self): # Define a file path #filePath = os.path.join(os.getcwd(), "plugins", "menu", "Interlinear_Data.csv") filePath = self.getFilePath("csv") if filePath: # Format data fileContent = '"{0}"'.format('","'.join(self.headers)) if self.results: for result in self.results: row = [ str(item) if index < 5 else item for index, item in enumerate(result) ] fileContent += '\n"{0}"'.format('","'.join(row)) # Write data into file with open(filePath, "w") as fileObj: fileObj.write(fileContent) self.openFile(filePath) def getFilePath(self, fileExtension="xlsx"): from qtpy.QtWidgets import QFileDialog defaultName = "Interlinear_Data.{0}".format(fileExtension) options = QFileDialog.Options() filePath, *_ = QFileDialog.getSaveFileName( self, config.thisTranslation["note_saveAs"], defaultName, "Spreadsheet File (*.{0})".format(fileExtension), "", options) if filePath: filePath = filePath.replace(" ", "_") if not filePath.endswith(".{0}".format(fileExtension)): filePath = "{0}.{1}".format(filePath, fileExtension) return filePath else: return "" def openFile(self, filePath): import platform, subprocess, os if platform.system() == "Linux": subprocess.Popen([config.open, filePath]) elif platform.system() == "Windows": try: subprocess("excel.exe {0}".format(filePath), shell=True) except: try: subprocess("start {0}".format(filePath), shell=True) except: pass else: os.system("{0} {1}".format(config.open, filePath))
class EditGuiLanguageFileDialog(QDialog): def __init__(self, parent, language): super(EditGuiLanguageFileDialog, self).__init__() self.parent = parent self.language = language self.reference = LanguageUtil.loadTranslation( config.referenceTranslation) targetLanguage = LanguageUtil.loadTranslation(language) self.languages = [] self.toolTips = [] # Cannot use the following two lines directly because target language file and reference file are most likely in different order #self.languages = [(key, value.replace("\n", "\\n")) for key, value in targetLanguage.items()] #self.toolTips = [(value.replace("\n", "\\n"), value.replace("\n", "\\n")) for value in self.reference.values()] for key in self.reference: if key in targetLanguage: self.languages.append( [key, targetLanguage[key].replace("\n", "\\n")]) self.toolTips.append([ self.reference[key].replace("\n", "\\n"), self.reference[key].replace("\n", "\\n") ]) self.setWindowTitle("Edit GUI Language File") self.setMinimumWidth(1000) self.setMinimumHeight(600) self.layout = QVBoxLayout() row = QHBoxLayout() self.filterEntry1 = QLineEdit() self.filterEntry1.textChanged.connect(self.filterChanged1) row.addWidget(self.filterEntry1) self.filterEntry2 = QLineEdit() self.filterEntry2.textChanged.connect(self.filterChanged2) row.addWidget(self.filterEntry2) self.layout.addLayout(row) self.table = QTableView() self.model = DisplayLanguagesModel(self, self.languages, self.toolTips) self.table.setModel(self.model) self.table.resizeColumnsToContents() self.table.setSortingEnabled(True) self.table.doubleClicked.connect(self.clickedRow) self.layout.addWidget(self.table) buttons = QDialogButtonBox.Ok | QDialogButtonBox.Cancel self.buttonBox = QDialogButtonBox(buttons) self.buttonBox.accepted.connect(self.save) self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.reject) self.layout.addWidget(self.buttonBox) self.setLayout(self.layout) def clickedRow(self, index): row = self.model.getRow(index.row()) (key, value) = row width = len(value) keyDisplay = key + '\n\n' + self.reference[key] + "\n" newValue, ok = QInputDialog.getText(self, 'Translation', keyDisplay, QLineEdit.Normal, value) if ok: self.model.list[index.row()] = (key, newValue) for item in self.model.fullList: if item[0] == key: item[1] = newValue break def save(self): LanguageUtil.saveLanguageFile(self.language, self.model.fullList) if self.language == config.displayLanguage: if self.parent is not None: for item in self.model.fullList: key = item[0] newValue = item[1] config.thisTranslation[key] = newValue self.parent.setupMenuLayout(config.menuLayout) self.parent.reloadControlPanel(False) def filterChanged1(self, text): self.model.filter(0, text) self.filterEntry2.setText("") def filterChanged2(self, text): self.model.filter(1, text) self.filterEntry1.setText("")
class SampleLogsView(QSplitter): """Sample Logs View This contains a table of the logs, a plot of the currently selected logs, and the statistics of the selected log. """ def __init__(self, presenter, parent = None, name = '', isMD=False, noExp = 0): super(SampleLogsView, self).__init__(parent) self.presenter = presenter self.setWindowTitle("{} sample logs".format(name)) self.setWindowFlags(Qt.Window) # Create sample log table self.table = QTableView() self.table.setSelectionBehavior(QAbstractItemView.SelectRows) self.table.clicked.connect(self.presenter.clicked) self.table.doubleClicked.connect(self.presenter.doubleClicked) self.table.contextMenuEvent = self.tableMenu self.addWidget(self.table) frame_right = QFrame() layout_right = QVBoxLayout() #Add full_time and experimentinfo options layout_options = QHBoxLayout() if isMD: layout_options.addWidget(QLabel("Experiment Info #")) self.experimentInfo = QSpinBox() self.experimentInfo.setMaximum(noExp-1) self.experimentInfo.valueChanged.connect(self.presenter.changeExpInfo) layout_options.addWidget(self.experimentInfo) self.full_time = QCheckBox("Relative Time") self.full_time.setChecked(True) self.full_time.stateChanged.connect(self.presenter.plot_logs) layout_options.addWidget(self.full_time) layout_right.addLayout(layout_options) # Sample log plot self.fig = Figure() self.canvas = FigureCanvas(self.fig) self.canvas.setSizePolicy(QSizePolicy.Expanding,QSizePolicy.Expanding) self.canvas.mpl_connect('button_press_event', self.presenter.plot_clicked) self.ax = self.fig.add_subplot(111, projection='mantid') layout_right.addWidget(self.canvas) # Sample stats self.create_stats_widgets() layout_stats = QFormLayout() layout_stats.addRow('', QLabel("Log Statistics")) layout_stats.addRow('Min:', self.stats_widgets["minimum"]) layout_stats.addRow('Max:', self.stats_widgets["maximum"]) layout_stats.addRow('Mean:', self.stats_widgets["mean"]) layout_stats.addRow('Median:', self.stats_widgets["median"]) layout_stats.addRow('Std Dev:', self.stats_widgets["standard_deviation"]) layout_stats.addRow('Time Avg:', self.stats_widgets["time_mean"]) layout_stats.addRow('Time Std Dev:', self.stats_widgets["time_standard_deviation"]) layout_stats.addRow('Duration:', self.stats_widgets["duration"]) layout_right.addLayout(layout_stats) frame_right.setLayout(layout_right) self.addWidget(frame_right) self.setStretchFactor(0,1) self.resize(1200,800) self.show() def tableMenu(self, event): """Right click menu for table, can plot or print selected logs""" menu = QMenu(self) plotAction = menu.addAction("Plot selected") plotAction.triggered.connect(self.presenter.new_plot_logs) plotAction = menu.addAction("Print selected") plotAction.triggered.connect(self.presenter.print_selected_logs) menu.exec_(event.globalPos()) def set_model(self, model): """Set the model onto the table""" self.model = model self.table.setModel(self.model) self.table.resizeColumnsToContents() self.table.horizontalHeader().setSectionResizeMode(2, QHeaderView.Stretch) def plot_selected_logs(self, ws, exp, rows): """Update the plot with the selected rows""" self.ax.clear() self.create_ax_by_rows(self.ax, ws, exp, rows) self.fig.canvas.draw() def new_plot_selected_logs(self, ws, exp, rows): """Create a new plot, in a separate window for selected rows""" fig, ax = plt.subplots(subplot_kw={'projection': 'mantid'}) self.create_ax_by_rows(ax, ws, exp, rows) fig.show() def create_ax_by_rows(self, ax, ws, exp, rows): """Creates the plots for given rows onto axis ax""" for row in rows: log_text = self.get_row_log_name(row) ax.plot(ws, LogName=log_text, label=log_text, marker='.', FullTime=not self.full_time.isChecked(), ExperimentInfo=exp) ax.set_ylabel('') if ax.get_legend_handles_labels()[0]: ax.legend() def get_row_log_name(self, i): """Returns the log name of particular row""" return str(self.model.item(i, 0).text()) def get_exp(self): """Get set experiment info number""" return self.experimentInfo.value() def get_selected_row_indexes(self): """Return a list of selected row from table""" return [row.row() for row in self.table.selectionModel().selectedRows()] def set_selected_rows(self, rows): """Set seleceted rows in table""" mode = QItemSelectionModel.Select | QItemSelectionModel.Rows for row in rows: self.table.selectionModel().select(self.model.index(row, 0), mode) def create_stats_widgets(self): """Creates the statistics widgets""" self.stats_widgets = {"minimum": QLineEdit(), "maximum": QLineEdit(), "mean": QLineEdit(), "median": QLineEdit(), "standard_deviation": QLineEdit(), "time_mean": QLineEdit(), "time_standard_deviation": QLineEdit(), "duration": QLineEdit()} for widget in self.stats_widgets.values(): widget.setReadOnly(True) def set_statistics(self, stats): """Updates the statistics widgets from stats dictionary""" for param in self.stats_widgets.keys(): self.stats_widgets[param].setText('{:.6}'.format(getattr(stats, param))) def clear_statistics(self): """Clears the values in statistics widgets""" for widget in self.stats_widgets.values(): widget.clear()
class ConfigManagerWindow(SiriusMainWindow): """Window to manage offline configuration of BO and SI devices. This window allows the user to create new configurations as well as interpolate or apply a tune or chromaticity delta to a configuration. """ NEW_CONFIGURATION = 0 def __init__(self, config_type, parent=None): """Init UI.""" super(ConfigManagerWindow, self).__init__(parent) self._config_type = config_type self._model = ConfigModel(self._config_type) self._delegate = ConfigDelegate() self._setup_ui() self.ld_cur_state_btn.clicked.connect(self._loadCurrentConfiguration) self.ld_config_btn.clicked.connect(self._addConfiguration) self.delete_config_btn.clicked.connect(self._removeConfiguration) self.setGeometry(100, 100, 1600, 900) self.setWindowTitle("Configuration Manager") self.show() def _setup_ui(self): self.central_widget = QWidget() self.central_widget.layout = QHBoxLayout() self.button_box = QVBoxLayout() self.ld_cur_state_btn = QPushButton("Load Current State") self.ld_config_btn = QPushButton("Load Configuration") self.ld_config_btn.setShortcut(QKeySequence.New) self.delete_config_btn = QPushButton("Delete Configuration") self.button_box.addWidget(self.delete_config_btn) self.button_box.addWidget(self.ld_config_btn) self.button_box.addWidget(self.ld_cur_state_btn) self.button_box.addStretch() # TableView self.table = QTableView(self) self.table.setModel(self._model) self.table.setItemDelegate(self._delegate) # self.table.setSelectionBehavior(QAbstractItemView.SelectColumns) self.table.setContextMenuPolicy(Qt.CustomContextMenu) self.table.customContextMenuRequested.connect(self._showHeaderMenu) self.table.resizeColumnsToContents() self.table.resizeRowsToContents() # TableView Headers self.headers = self.table.horizontalHeader() self.headers.setContextMenuPolicy(Qt.CustomContextMenu) self.headers.customContextMenuRequested.connect(self._showHeaderMenu) self.central_widget.layout.addLayout(self.button_box) self.central_widget.layout.addWidget(self.table) self.central_widget.setLayout(self.central_widget.layout) # Set widget self.setCentralWidget(self.central_widget) def closeEvent(self, event): """Close window. The user is warned if there are any unsaved changes. """ columns = list(range(len(self._model.configurations))) columns.sort(reverse=True) if not self._closeConfigurations(columns): event.ignore() def keyPressEvent(self, event): """Override keyPressEvent. Ctrl+S - Save changes Ctrl+W - Close configuration on focus F2 - Rename configuration on focus Ctrl+Z - Undo Ctrl+R - Redo """ if event.key() == Qt.Key_S: self._saveChanges() return if event.key() == Qt.Key_W: self._closeConfigurationOnFocus() return if event.key() == Qt.Key_F2: self._renameOnFocus() return if event.key() == Qt.Key_Z: print(self._model._undo) if len(self._model._undo) > 0: self._model._undo.pop()[1]() return if event.key() == Qt.Key_R: if len(self._model._redo) > 0: self._model._redo.pop()[1]() return @Slot(QPoint) def _showHeaderMenu(self, point): column = self.headers.logicalIndexAt(point.x()) if column == -1: return menu = QMenu(self) # Actions cols = self.table.selectionModel().selectedColumns() if len(cols) != 2 or column not in [col.column() for col in cols]: self.table.selectColumn(column) menu.aboutToHide.connect(lambda: self.table.clearSelection()) save = QAction("Save", menu) save.triggered.connect(lambda: self._saveConfiguration(column)) save_all = QAction("Save all", menu) save_all.triggered.connect(lambda: self._saveChanges()) save_all.setShortcut(QKeySequence.Save) rename = QAction("Rename", menu) rename.triggered.connect(lambda: self._renameConfiguration(column)) close = QAction("Close", menu) close.triggered.connect(lambda: self._closeConfiguration(column)) close.setShortcut(QKeySequence.Close) close_right = QAction("Close to the right", menu) close_right.triggered.connect( lambda: self._closeConfigurationsToTheRight(column)) close_others = QAction("Close other", menu) close_others.triggered.connect( lambda: self._closeOtherConfigurations(column)) close_all = QAction("Close all", menu) close_all.triggered.connect(lambda: self._closeAllConfigurations()) tune = QAction("Tune", menu) tune.triggered.connect(lambda: self._tuneConfiguration(column)) menu.addActions([save, save_all]) menu.addSeparator() menu.addActions([rename]) menu.addSeparator() menu.addActions([close, close_right, close_others, close_all]) menu.addSeparator() menu.addActions([tune]) else: bar = QAction("Interpolate", menu) bar.triggered.connect(lambda: self._barConfiguration(cols)) menu.addAction(bar) vheader_offset = self.table.verticalHeader().width() point.setX(point.x() + vheader_offset) menu.popup(self.mapToGlobal(point)) # ContextMenu Actions @Slot(int) def _saveConfiguration(self, column): try: self._model.saveConfiguration(column) return True except Exception as e: QMessageBox(QMessageBox.Warning, "Failed to save data", "{}, {}".format(e, type(e))).exec_() return False @Slot(int) def _renameConfiguration(self, column): new_name, ok = QInputDialog.getText(self, "New name", "Rename to:") if ok and new_name: return self._model.renameConfiguration(column, new_name) @Slot(int) def _closeConfiguration(self, column): self._closeConfigurations([column]) @Slot(int) def _closeConfigurationsToTheRight(self, column): columns = list() i = len(self._model.configurations) - 1 while i > column: columns.append(i) i -= 1 self._closeConfigurations(columns) @Slot(int) def _closeOtherConfigurations(self, column): columns = list() i = len(self._model.configurations) - 1 while i >= 0: if i == column: i -= 1 continue columns.append(i) i -= 1 self._closeConfigurations(columns) @Slot() def _closeAllConfigurations(self): columns = list() i = len(self._model.configurations) - 1 while i >= 0: columns.append(i) i -= 1 self._closeConfigurations(columns) def _closeConfigurations(self, columns): save = self._maybeSaveChanges(columns) if save == QMessageBox.Discard: for column in columns: self._model.cleanUndo(column) self._model.closeConfiguration(column) return True elif save == QMessageBox.Save: for column in columns: if self._saveConfiguration(column): self._model.cleanUndo(column) self._model.closeConfiguration(column) else: return False return True else: return False @Slot(int) def _tuneConfiguration(self, column): dlg = TuneDlg(self) ok1 = dlg.exec_() if ok1: # Get Matrix Calculate deltaK and show to user tune = [dlg.tune_x.value(), dlg.tune_y.value()] try: inv_matrix = self._model.getTuneMatrix() except Exception as e: self._showWarningBox("{}".format(e), "Failed to retrieve tune matrix") else: delta_f = tune[0] * inv_matrix[0][0] + tune[1] * inv_matrix[0][ 1] delta_d = tune[0] * inv_matrix[1][0] + tune[1] * inv_matrix[1][ 1] # config_name, ok2 = QInputDialog.getText( # self, "Select value", "New Configuration Name:") # if ok2: # if not config_name: proceed = QMessageBox(QMessageBox.Question, "Delta K", ("\u0394K<sub>d</sub> = {:1.3f}<br>" "\u0394K<sub>f</sub> = {:1.3f}<br>" "Proceed?").format(delta_d, delta_f), QMessageBox.Ok | QMessageBox.Cancel, self).exec_() if proceed == QMessageBox.Ok: config_name = self._getNextName() self._model.deriveConfiguration(config_name, column, ConfigModel.TUNE, [delta_d, delta_f]) @Slot(int) def _barConfiguration(self, cols): if len(cols) != 2: raise SystemError("Must interpolate 2 columns") new_name, ok = QInputDialog.getText(self, "New name", "Rename to:") if ok: if not new_name: new_name = self._getNextName() self._model.interpolateConfiguration(new_name, cols[0].column(), cols[1].column()) # Window menu slots @Slot() def _addConfiguration(self): try: configs = self._model.getConfigurations() except Exception as e: self._showWarningBox(e, "Failed to retrieve configurations") else: if configs: options = [item["name"] for item in configs] config_name, ok = QInputDialog.getItem( self, "Available Configurations", "Select a configuration:", options, 0, False) if ok and config_name: if not self._isConfigurationLoaded(config_name): self._model.loadConfiguration(name=config_name) else: QMessageBox( QMessageBox.Information, "Configuration already loaded", "Configuration is already loaded.").exec_() # Highlight new column; or the one that is already loaded col = self._model.getConfigurationColumn(config_name) self.table.selectColumn(col) else: self._showMessageBox("No configuration found") return @Slot() def _removeConfiguration(self): try: configs = self._model.getConfigurations() except Exception as e: self._showWarningBox(e, "Failed to retrieve configurations") else: if configs: # Show configs available options = [item["name"] for item in configs] config, ok = QInputDialog.getItem(self, "Available Configurations", "Select a configuration:", options, 0, False) if ok and config: # Ask for confirmation if self._isConfigurationLoaded(config): msg = ("Configuration is currenty loaded." "Delete it anyway?") else: msg = ("This will permanently delete configuration {}." "Proceed?").format(config) if self._showDialogBox(msg) == QMessageBox.Cancel: return # Delete configuration config = configs[options.index(config)] try: self._model.deleteConfiguration(config) except Exception as e: self._showWarningBox(e) else: self._showMessageBox( "Configuration {} was deleted.".format( config['name'])) else: self._showMessageBox("No configuration found") return @Slot() def _loadCurrentConfiguration(self): try: t = LoadingThread(self._getNextName(), self._model._vertical_header, self) dlg = \ LoadingDialog("Loading", len(self._model._vertical_header), self) t.taskUpdated.connect(dlg.update) t.taskFinished.connect(dlg.done) t.start() dlg.exec_() except Exception as e: self._showWarningBox("{}".format(e)) # Actions binded with keys def _saveChanges(self): for column in range(len(self._model.configurations)): self._saveConfiguration(column) def _closeConfigurationOnFocus(self): cols = self.table.selectionModel().selectedColumns() columns = list() for col in cols: columns.append(col.column()) columns.sort(reverse=True) self._closeConfigurations(columns) def _renameOnFocus(self): cols = self.table.selectionModel().selectedColumns() if len(cols) == 1: self._renameConfiguration(cols[0].column()) # Helpers def _isConfigurationLoaded(self, config_name): ret = self._model.getConfigurationColumn(config_name) if ret == -1: return False return True def _getNextName(self): # Treat if there already exist saved configuration with this name configs = self._model.getConfigurations(deleted=None) configs = [item["name"] for item in configs] configs.extend([item.name for item in self._model.configurations]) new_name = 'config-{}'.format(self.NEW_CONFIGURATION) while new_name in configs: self.NEW_CONFIGURATION += 1 new_name = 'config-{}'.format(self.NEW_CONFIGURATION) return new_name def _maybeSaveChanges(self, columns): ask_to_save = False for column in columns: if self._model.configurations[column].dirty: ask_to_save = True break # If nothing to save, will close all columns if not ask_to_save: return QMessageBox.Discard # Ask if user wants to save changes msg_box = QMessageBox( QMessageBox.Question, "There are unsaved changes", "Keep changes?", QMessageBox.Save | QMessageBox.Cancel | QMessageBox.Discard, self) return msg_box.exec_() def _showWarningBox(self, message, title="Warning"): QMessageBox(QMessageBox.Warning, title, '{}'.format(message)).exec_() def _showMessageBox(self, message, title="Message"): return QMessageBox(QMessageBox.Information, title, message).exec_() def _showDialogBox(self, message, title="Dialog"): return QMessageBox(QMessageBox.Information, title, message, QMessageBox.Ok | QMessageBox.Cancel).exec_()
class LibraryCatalogDialog(QDialog): def __init__(self, parent): super().__init__() self.parent = parent self.setWindowTitle(config.thisTranslation["libraryCatalog"]) self.setMinimumSize(700, 500) self.setupVariables() self.setupUI() def setupVariables(self): self.isUpdating = False self.catalogEntryId = None self.localCatalog = CatalogUtil.loadLocalCatalog() self.remoteCatalog = gitHubRepoCacheData self.localCatalogData = self.getLocalCatalogItems() self.remoteCatalogData = self.getRemoteCatalogItems() self.location = "local" self.textButtonStyle = "QPushButton {background-color: #333972; color: white;} QPushButton:hover {background-color: #333972;} QPushButton:pressed { background-color: #515790;}" def setupUI(self): mainLayout = QVBoxLayout() filterLayout = QHBoxLayout() filterLayout.addWidget(QLabel(config.thisTranslation["menu5_search"])) self.filterEntry = QLineEdit() self.filterEntry.setClearButtonEnabled(True) self.filterEntry.textChanged.connect(self.resetItems) filterLayout.addWidget(self.filterEntry) mainLayout.addLayout(filterLayout) self.searchTypeBox = QGroupBox("") locationLayout = QHBoxLayout() self.localRadioButton = QRadioButton("Local") self.localRadioButton.setChecked(True) self.localRadioButton.toggled.connect( lambda: self.setLocation("local")) locationLayout.addWidget(self.localRadioButton) self.remoteRadioButton = QRadioButton("Remote") self.remoteRadioButton.toggled.connect( lambda: self.setLocation("remote")) locationLayout.addWidget(self.remoteRadioButton) self.searchTypeBox.setLayout(locationLayout) mainLayout.addWidget(self.searchTypeBox) typesLayout = QHBoxLayout() button = QPushButton("All") button.setStyleSheet(self.textButtonStyle) button.clicked.connect(lambda: self.selectAllTypes(True)) typesLayout.addWidget(button) button = QPushButton("None") button.setStyleSheet(self.textButtonStyle) button.clicked.connect(lambda: self.selectAllTypes(False)) typesLayout.addWidget(button) self.bookCheckbox = QCheckBox("BOOK") self.bookCheckbox.setChecked(True) self.bookCheckbox.stateChanged.connect(self.resetItems) typesLayout.addWidget(self.bookCheckbox) self.pdfCheckbox = QCheckBox("PDF") self.pdfCheckbox.setChecked(True) self.pdfCheckbox.stateChanged.connect(self.resetItems) typesLayout.addWidget(self.pdfCheckbox) self.docxCheckbox = QCheckBox("DOCX") self.docxCheckbox.setChecked(True) self.docxCheckbox.stateChanged.connect(self.resetItems) typesLayout.addWidget(self.docxCheckbox) self.devotionalCheckbox = QCheckBox("DEVOTIONAL") self.devotionalCheckbox.setChecked(True) self.devotionalCheckbox.stateChanged.connect(self.resetItems) typesLayout.addWidget(self.devotionalCheckbox) self.commCheckbox = QCheckBox("COMM") self.commCheckbox.setChecked(True) self.commCheckbox.stateChanged.connect(self.resetItems) typesLayout.addWidget(self.commCheckbox) self.mp3Checkbox = QCheckBox("MP3") self.mp3Checkbox.setChecked(True) self.mp3Checkbox.stateChanged.connect(self.resetItems) typesLayout.addWidget(self.mp3Checkbox) self.mp4Checkbox = QCheckBox("MP4") self.mp4Checkbox.setChecked(True) self.mp4Checkbox.stateChanged.connect(self.resetItems) typesLayout.addWidget(self.mp4Checkbox) mainLayout.addLayout(typesLayout) self.dataView = QTableView() self.dataView.clicked.connect(self.itemClicked) self.dataView.setEditTriggers(QAbstractItemView.NoEditTriggers) self.dataView.setSortingEnabled(True) self.dataViewModel = QStandardItemModel(self.dataView) self.dataView.setModel(self.dataViewModel) self.resetItems() mainLayout.addWidget(self.dataView) buttonLayout = QHBoxLayout() self.openButton = QPushButton(config.thisTranslation["open"]) self.openButton.setEnabled(True) self.openButton.setStyleSheet(self.textButtonStyle) self.openButton.clicked.connect(self.open) buttonLayout.addWidget(self.openButton) self.downloadButton = QPushButton(config.thisTranslation["download"]) self.downloadButton.setEnabled(False) self.downloadButton.clicked.connect(self.download) buttonLayout.addWidget(self.downloadButton) button = QPushButton(config.thisTranslation["close"]) button.setStyleSheet(self.textButtonStyle) button.clicked.connect(self.close) buttonLayout.addWidget(button) mainLayout.addLayout(buttonLayout) self.setLayout(mainLayout) def setLocation(self, location): self.location = location self.resetItems() if location == "local": self.openButton.setEnabled(True) self.openButton.setStyleSheet(self.textButtonStyle) self.downloadButton.setEnabled(False) else: self.openButton.setEnabled(False) self.openButton.setStyleSheet("") self.downloadButton.setEnabled(True) def selectAllTypes(self, value): self.pdfCheckbox.setChecked(value) self.mp3Checkbox.setChecked(value) self.mp4Checkbox.setChecked(value) self.bookCheckbox.setChecked(value) self.docxCheckbox.setChecked(value) self.devotionalCheckbox.setChecked(value) self.commCheckbox.setChecked(value) def getLocalCatalogItems(self): return self.getCatalogItems(self.localCatalog) def getRemoteCatalogItems(self): return self.getCatalogItems(self.remoteCatalog) def getCatalogItems(self, catalog): data = {} pdfCount = 0 mp3Count = 0 mp4Count = 0 bookCount = 0 docxCount = 0 commCount = 0 lexCount = 0 devotionalCount = 0 for filename, type, directory, file, description, repo, installDirectory, sha in catalog: id = "UNKNOWN" if type == "PDF": pdfCount += 1 id = "{0}-{1}".format(type, pdfCount) elif type == "MP3": mp3Count += 1 id = "{0}-{1}".format(type, mp3Count) elif type == "MP4": mp4Count += 1 id = "{0}-{1}".format(type, mp4Count) elif type == "BOOK": bookCount += 1 id = "{0}-{1}".format(type, bookCount) elif type == "DOCX": docxCount += 1 id = "{0}-{1}".format(type, docxCount) elif type == "COMM": commCount += 1 id = "{0}-{1}".format(type, commCount) elif type == "LEX": lexCount += 1 id = "{0}-{1}".format(type, lexCount) elif type == "DEVOTIONAL": devotionalCount += 1 id = "{0}-{1}".format(type, devotionalCount) data[id] = [ id, filename, type, directory, file, description, repo, installDirectory, sha ] return data def resetItems(self): self.isUpdating = True self.dataViewModel.clear() filterEntry = self.filterEntry.text().lower() rowCount = 0 colCount = 0 catalogData = self.localCatalogData if self.location == "remote": catalogData = self.remoteCatalogData for id, value in catalogData.items(): id2, filename, type, directory, file, description, repo, installDirectory, sha = value if (filterEntry == "" or filterEntry in filename.lower() or filterEntry in description.lower()): if (not self.pdfCheckbox.isChecked() and type == "PDF") or \ (not self.mp3Checkbox.isChecked() and type == "MP3") or \ (not self.mp4Checkbox.isChecked() and type == "MP4") or \ (not self.bookCheckbox.isChecked() and type == "BOOK") or \ (not self.docxCheckbox.isChecked() and type == "DOCX") or \ (not self.devotionalCheckbox.isChecked() and type == "DEVOTIONAL") or \ (not self.commCheckbox.isChecked() and type == "COMM"): continue enable = True if self.location == "remote": installDirectory = os.path.join(config.marvelData, installDirectory) if FileUtil.regexFileExists( "{0}.*".format(GithubUtil.getShortname(filename)), installDirectory): enable = False item = QStandardItem(id) item.setEnabled(enable) self.dataViewModel.setItem(rowCount, colCount, item) colCount += 1 item = QStandardItem(file) item.setEnabled(enable) self.dataViewModel.setItem(rowCount, colCount, item) colCount += 1 item = QStandardItem(directory) item.setEnabled(enable) self.dataViewModel.setItem(rowCount, colCount, item) colCount += 1 # item = QStandardItem(description) # self.dataViewModel.setItem(rowCount, colCount, item) # colCount += 1 # add row count rowCount += 1 colCount = 0 self.dataViewModel.setHorizontalHeaderLabels([ "#", config.thisTranslation["file"], config.thisTranslation["directory"], # config.thisTranslation["description"] ]) self.dataView.resizeColumnsToContents() self.isUpdating = False def itemClicked(self, index): selectedRow = index.row() self.catalogEntryId = self.dataViewModel.item(selectedRow, 0).text() if self.location == "remote": item = self.remoteCatalogData[self.catalogEntryId] id, filename, type, directory, file, description, repo, installDirectory, sha = item installDirectory = os.path.join(config.marvelData, installDirectory) if FileUtil.regexFileExists( "{0}.*".format(GithubUtil.getShortname(filename)), installDirectory): self.downloadButton.setEnabled(False) self.downloadButton.setStyleSheet("") else: self.downloadButton.setEnabled(True) self.downloadButton.setStyleSheet(self.textButtonStyle) def displayMessage(self, message="", title="UniqueBible"): QMessageBox.information(self, title, message) def saveRemoteCatalogToCache(self): data = CatalogUtil.loadRemoteCatalog() with open("util/GitHubRepoCache.py", "w", encoding="utf-8") as fileObj: fileObj.write("gitHubRepoCacheData = {0}\n".format( pprint.pformat(data))) def fixDirectory(self, directory, type): if type == "PDF": directory = directory.replace(config.marvelData, "") directory = directory.replace("/pdf", "") if len(directory) > 0 and not directory.endswith("/"): directory += "/" if len(directory) > 0 and directory.startswith("/"): directory = directory[1:] return directory def open(self): item = self.localCatalogData[self.catalogEntryId] id, filename, type, directory, file, description, repo, installDirectory, sha = item directory = self.fixDirectory(directory, type) command = "" if type == "PDF": command = "PDF:::{0}{1}".format(directory, file) elif type == "MP3": command = "VLC:::{0}{1}".format(directory, file) elif type == "MP4": command = "VLC:::{0}{1}".format(directory, file) elif type == "BOOK": if file.endswith(".book"): file = file.replace(".book", "") config.booksFolder = directory command = "BOOK:::{0}".format(file) elif type == "COMM": file = file.replace(".commentary", "") file = file[1:] config.commentariesFolder = directory command = "COMMENTARY:::{0}:::{1} {2}".format( file, BibleBooks.eng[str(config.mainB)][0], config.mainC) elif type == "DOCX": command = "DOCX:::{0}".format(file) elif type == "DEVOTIONAL": file = file.replace(".devotional", "") command = "DEVOTIONAL:::{0}".format(file) self.parent.runTextCommand(command) def download(self): self.downloadButton.setEnabled(False) self.downloadButton.setStyleSheet("") item = self.remoteCatalogData[self.catalogEntryId] id, filename, type, directory, file, description, repo, installDirectory, sha = item github = GithubUtil(repo) installDirectory = os.path.join(config.marvelData, installDirectory) file = os.path.join(installDirectory, filename + ".zip") github.downloadFile(file, sha) with zipfile.ZipFile(file, 'r') as zipped: zipped.extractall(installDirectory) os.remove(file) self.displayMessage(filename + " " + config.thisTranslation["message_installed"]) self.localCatalog = CatalogUtil.reloadLocalCatalog() self.localCatalogData = self.getLocalCatalogItems() self.resetItems()
class SampleLogsView(QSplitter): """Sample Logs View This contains a table of the logs, a plot of the currently selected logs, and the statistics of the selected log. """ def __init__(self, presenter, parent = None, name = '', isMD=False, noExp = 0): super(SampleLogsView, self).__init__(parent) self.presenter = presenter self.setWindowTitle("{} sample logs".format(name)) self.setWindowFlags(Qt.Window) self.setAttribute(Qt.WA_DeleteOnClose, True) # left hand side self.frame_left = QFrame() layout_left = QVBoxLayout() # add a spin box for MD workspaces if isMD: layout_mult_expt_info = QHBoxLayout() layout_mult_expt_info.addWidget(QLabel("Experiment Info #")) self.experimentInfo = QSpinBox() self.experimentInfo.setMaximum(noExp-1) self.experimentInfo.valueChanged.connect(self.presenter.changeExpInfo) layout_mult_expt_info.addWidget(self.experimentInfo) layout_mult_expt_info.addSpacerItem(QSpacerItem(10, 10, QSizePolicy.Expanding)) layout_left.addLayout(layout_mult_expt_info) # Create sample log table self.table = QTableView() self.table.setSelectionBehavior(QAbstractItemView.SelectRows) self.table.doubleClicked.connect(self.presenter.doubleClicked) self.table.contextMenuEvent = self.tableMenu layout_left.addWidget(self.table) self.frame_left.setLayout(layout_left) self.addWidget(self.frame_left) #right hand side self.frame_right = QFrame() layout_right = QVBoxLayout() #Add full_time and experimentinfo options layout_options = QHBoxLayout() if isMD: layout_options.addWidget(QLabel("Experiment Info #")) self.experimentInfo = QSpinBox() self.experimentInfo.setMaximum(noExp-1) self.experimentInfo.valueChanged.connect(self.presenter.changeExpInfo) layout_options.addWidget(self.experimentInfo) #check boxes self.full_time = QCheckBox("Relative Time") self.full_time.setToolTip( "Shows relative time in seconds from the start of the run.") self.full_time.setChecked(True) self.full_time.stateChanged.connect(self.presenter.plot_logs) layout_options.addWidget(self.full_time) self.show_filtered = QCheckBox("Filtered Data") self.show_filtered.setToolTip( "Filtered data only shows data while running and in this period.\nInvalid values are also filtered.") self.show_filtered.setChecked(True) self.show_filtered.stateChanged.connect(self.presenter.filtered_changed) layout_options.addWidget(self.show_filtered) self.spaceItem = QSpacerItem(10, 10, QSizePolicy.Expanding) layout_options.addSpacerItem(self.spaceItem) layout_right.addLayout(layout_options) # Sample log plot self.fig = Figure() self.canvas = FigureCanvas(self.fig) self.canvas.setSizePolicy(QSizePolicy.Expanding,QSizePolicy.Expanding) self.canvas.mpl_connect('button_press_event', self.presenter.plot_clicked) self.ax = self.fig.add_subplot(111, projection='mantid') layout_right.addWidget(self.canvas) # Sample stats self.create_stats_widgets() layout_stats = QFormLayout() layout_stats.addRow('', QLabel("Log Statistics")) layout_stats.addRow('Min:', self.stats_widgets["minimum"]) layout_stats.addRow('Max:', self.stats_widgets["maximum"]) layout_stats.addRow('Time Avg:', self.stats_widgets["time_mean"]) layout_stats.addRow('Time Std Dev:', self.stats_widgets["time_standard_deviation"]) layout_stats.addRow('Mean (unweighted):', self.stats_widgets["mean"]) layout_stats.addRow('Median (unweighted):', self.stats_widgets["median"]) layout_stats.addRow('Std Dev:', self.stats_widgets["standard_deviation"]) layout_stats.addRow('Duration:', self.stats_widgets["duration"]) layout_right.addLayout(layout_stats) self.frame_right.setLayout(layout_right) self.addWidget(self.frame_right) self.setStretchFactor(0,1) self.resize(1200,800) self.show() def closeEvent(self, event): self.deleteLater() super(SampleLogsView, self).closeEvent(event) def tableMenu(self, event): """Right click menu for table, can plot or print selected logs""" menu = QMenu(self) plotAction = menu.addAction("Plot selected") plotAction.triggered.connect(self.presenter.new_plot_logs) plotAction = menu.addAction("Print selected") plotAction.triggered.connect(self.presenter.print_selected_logs) menu.exec_(event.globalPos()) def set_model(self, model): """Set the model onto the table""" self.model = model self.table.setModel(self.model) self.table.resizeColumnsToContents() self.table.horizontalHeader().setSectionResizeMode(2, QHeaderView.Stretch) self.table.selectionModel().selectionChanged.connect(self.presenter.update) def show_plot_and_stats(self, show_plot_and_stats): """sets wether the plot and stats section should be visible""" if self.frame_right.isVisible() != show_plot_and_stats: # the desired state is nor the current state self.setUpdatesEnabled(False) current_width = self.frame_right.width() if current_width: self.last_width = current_width else: current_width = self.last_width if show_plot_and_stats: self.resize(self.width() + current_width, self.height()) else: self.resize(self.width() - current_width, self.height()) self.frame_right.setVisible(show_plot_and_stats) self.setUpdatesEnabled(True) def plot_selected_logs(self, ws, exp, rows): """Update the plot with the selected rows""" if self.frame_right.isVisible(): self.ax.clear() self.create_ax_by_rows(self.ax, ws, exp, rows) try: self.fig.canvas.draw() except ValueError as ve: #this can throw an error if the plot has recently been hidden, but the error does not matter if not str(ve).startswith("Image size of"): raise def new_plot_selected_logs(self, ws, exp, rows): """Create a new plot, in a separate window for selected rows""" fig, ax = plt.subplots(subplot_kw={'projection': 'mantid'}) self.create_ax_by_rows(ax, ws, exp, rows) fig.show() def create_ax_by_rows(self, ax, ws, exp, rows): """Creates the plots for given rows onto axis ax""" for row in rows: log_text = self.get_row_log_name(row) ax.plot(ws, LogName=log_text, label=log_text, FullTime=not self.full_time.isChecked(), Filtered=self.show_filtered.isChecked(), ExperimentInfo=exp) ax.set_ylabel('') if ax.get_legend_handles_labels()[0]: ax.legend() def set_log_controls(self,are_logs_filtered): """Sets log specific settings based on the log clicked on""" self.show_filtered.setEnabled(are_logs_filtered) def get_row_log_name(self, i): """Returns the log name of particular row""" return str(self.model.item(i, 0).text()) def get_exp(self): """Get set experiment info number""" return self.experimentInfo.value() def get_selected_row_indexes(self): """Return a list of selected row from table""" return [row.row() for row in self.table.selectionModel().selectedRows()] def set_selected_rows(self, rows): """Set seleceted rows in table""" mode = QItemSelectionModel.Select | QItemSelectionModel.Rows for row in rows: self.table.selectionModel().select(self.model.index(row, 0), mode) def create_stats_widgets(self): """Creates the statistics widgets""" self.stats_widgets = {"minimum": QLineEdit(), "maximum": QLineEdit(), "mean": QLineEdit(), "median": QLineEdit(), "standard_deviation": QLineEdit(), "time_mean": QLineEdit(), "time_standard_deviation": QLineEdit(), "duration": QLineEdit()} for widget in self.stats_widgets.values(): widget.setReadOnly(True) def set_statistics(self, stats): """Updates the statistics widgets from stats dictionary""" for param in self.stats_widgets.keys(): self.stats_widgets[param].setText('{:.6}'.format(getattr(stats, param))) def clear_statistics(self): """Clears the values in statistics widgets""" for widget in self.stats_widgets.values(): widget.clear()
class LiveFilterDialog(QDialog): JS_HIDE = """ count = 0; searchResultCount = document.getElementById("searchResultCount"); divs = document.querySelectorAll("div"); for (var i = 0, len = divs.length; i < len; i++) {{ div = divs[i]; div.hidden = {0}; count++; }}; if (searchResultCount) {{ searchResultCount.innerHTML = count; }} """ JS_SHOW = """ wordSets = [{0}]; count = 0; searchResultCount = document.getElementById("searchResultCount"); divs = document.querySelectorAll("div"); for (var i=0, len=divs.length; i < len; i++) {{ div = divs[i]; var found = true; for (var j=0, len2=wordSets.length; j < len2; j++) {{ wordSet = wordSets[j]; var regex; if (wordSet.startsWith("'")) {{ wordSet = wordSet.replace("'", ""); wordSet = wordSet.replace("'", ""); regex = new RegExp(wordSet); }} else {{ regex = new RegExp(wordSet, "i"); }} found &= regex.test(div.innerHTML); }} if (found) {{ div.hidden = false; count++; }} }}; if (searchResultCount) {{ searchResultCount.innerHTML = count; }} """ def __init__(self, parent): super().__init__() self.parent = parent self.setWindowTitle(config.thisTranslation["liveFilter"]) self.setMinimumSize(400, 400) self.selectedFilter = None self.selectedPattern = None self.settingBibles = False self.db = LiveFilterSqlite() self.filters = None self.saveReadFormattedBibles = config.readFormattedBibles if config.readFormattedBibles: self.parent.disableBiblesInParagraphs() self.setupUI() def setupUI(self): mainLayout = QVBoxLayout() title = QLabel(config.thisTranslation["liveFilter"]) mainLayout.addWidget(title) self.filtersTable = QTableView() self.filtersTable.setEnabled(True) self.filtersTable.setEditTriggers(QAbstractItemView.NoEditTriggers) self.filtersTable.setSortingEnabled(True) self.dataViewModel = QStandardItemModel(self.filtersTable) self.filtersTable.setModel(self.dataViewModel) self.dataViewModel.itemChanged.connect(self.filterSelectionChanged) self.selectionModel = self.filtersTable.selectionModel() self.selectionModel.selectionChanged.connect(self.handleSelection) mainLayout.addWidget(self.filtersTable) self.reloadFilters() buttonsLayout = QHBoxLayout() addButton = QPushButton(config.thisTranslation["add"]) addButton.clicked.connect(self.addNewFilter) buttonsLayout.addWidget(addButton) removeButton = QPushButton(config.thisTranslation["remove"]) removeButton.clicked.connect(self.removeFilter) buttonsLayout.addWidget(removeButton) editButton = QPushButton(config.thisTranslation["edit"]) editButton.clicked.connect(self.editFilter) buttonsLayout.addWidget(editButton) importButton = QPushButton(config.thisTranslation["import"]) importButton.clicked.connect(self.importFile) buttonsLayout.addWidget(importButton) buttonsLayout.addStretch() mainLayout.addLayout(buttonsLayout) buttons = QDialogButtonBox.Ok self.buttonBox = QDialogButtonBox(buttons) self.buttonBox.accepted.connect(self.accept) self.buttonBox.accepted.connect(self.close) self.buttonBox.rejected.connect(self.reject) mainLayout.addWidget(self.buttonBox) self.setLayout(mainLayout) def close(self): pass def reloadFilters(self): self.filters = self.db.getAll() self.dataViewModel.clear() rowCount = 0 for bible, description in self.filters: item = QStandardItem(bible) item.setToolTip(bible) item.setCheckable(True) self.dataViewModel.setItem(rowCount, 0, item) item = QStandardItem(description) self.dataViewModel.setItem(rowCount, 1, item) rowCount += 1 self.dataViewModel.setHorizontalHeaderLabels([ config.thisTranslation["filter2"], config.thisTranslation["pattern"] ]) self.filtersTable.resizeColumnsToContents() def handleSelection(self, selected, deselected): for item in selected: row = item.indexes()[0].row() filter = self.dataViewModel.item(row, 0) self.selectedFilter = filter.text() pattern = self.dataViewModel.item(row, 1) self.selectedPattern = pattern.text() def filterSelectionChanged(self, item): try: numChecked = 0 for index in range(self.dataViewModel.rowCount()): item = self.dataViewModel.item(index) if item.checkState() == Qt.Checked: numChecked += 1 if numChecked == 0: config.mainWindow.studyPage.runJavaScript( self.JS_HIDE.format("false")) else: sets = [] config.mainWindow.studyPage.runJavaScript( self.JS_HIDE.format("true")) for index in range(self.dataViewModel.rowCount()): item = self.dataViewModel.item(index) if item.checkState() == Qt.Checked: sets.append('"{0}"'.format(self.filters[index][1])) wordSets = ",".join(sets) js = self.JS_SHOW.format(wordSets) config.mainWindow.studyPage.runJavaScript(js) except Exception as e: print(str(e)) def addNewFilter(self): fields = [(config.thisTranslation["filter2"], ""), (config.thisTranslation["pattern"], "")] dialog = MultiLineInputDialog("New Filter", fields) if dialog.exec(): data = dialog.getInputs() self.db.insert(data[0], data[1]) self.reloadFilters() def removeFilter(self): reply = QMessageBox.question( self, "Delete", 'Delete {0} {1}'.format(self.selectedFilter, config.thisTranslation["filter2"]), QMessageBox.Yes | QMessageBox.No) if reply == QMessageBox.Yes: self.db.delete(self.selectedFilter) self.reloadFilters() def editFilter(self): fields = [(config.thisTranslation["filter2"], self.selectedFilter), (config.thisTranslation["pattern"], self.selectedPattern)] dialog = MultiLineInputDialog("Edit Filter", fields) if dialog.exec(): data = dialog.getInputs() self.db.delete(self.selectedFilter) self.db.insert(data[0], data[1]) self.reloadFilters() def importFile(self): options = QFileDialog.Options() filename, filtr = QFileDialog.getOpenFileName( self, config.thisTranslation["import"], config.thisTranslation["liveFilter"], "File (*.*)", "", options) if filename: try: with open(filename, errors='ignore') as f: for line in f: data = line.split(":::") filter = data[0].strip() pattern = data[1].strip() if self.db.checkFilterExists(filter): self.db.delete(filter) self.db.insert(filter, pattern) except Exception as e: print(e) self.reloadFilters()
class ConfigFlagsWindow(QDialog): def __init__(self, parent): super().__init__() self.parent = parent # set title self.setWindowTitle(config.thisTranslation["menu_config_flags"]) self.setMinimumSize(830, 500) # set variables self.setupVariables() # setup interface self.setupUI() def setupVariables(self): self.isUpdating = False def setupUI(self): mainLayout = QVBoxLayout() title = QLabel(config.thisTranslation["menu_config_flags"]) title.mouseReleaseEvent = self.openWiki mainLayout.addWidget(title) filterLayout = QHBoxLayout() filterLayout.addWidget(QLabel(config.thisTranslation["menu5_search"])) self.filterEntry = QLineEdit() self.filterEntry.textChanged.connect(self.resetItems) filterLayout.addWidget(self.filterEntry) mainLayout.addLayout(filterLayout) self.dataView = QTableView() self.dataView.setEditTriggers(QAbstractItemView.NoEditTriggers) self.dataView.setSortingEnabled(True) self.dataViewModel = QStandardItemModel(self.dataView) self.dataView.setModel(self.dataViewModel) self.resetItems() self.dataViewModel.itemChanged.connect(self.itemChanged) mainLayout.addWidget(self.dataView) buttonLayout = QHBoxLayout() button = QPushButton(config.thisTranslation["close"]) button.clicked.connect(self.close) buttonLayout.addWidget(button) button = QPushButton(config.thisTranslation["restoreAllDefaults"]) button.clicked.connect(self.restoreAllDefaults) buttonLayout.addWidget(button) mainLayout.addLayout(buttonLayout) self.setLayout(mainLayout) def getOptions(self): options = [ ("showControlPanelOnStartup", config.showControlPanelOnStartup, self.showControlPanelOnStartupChanged, False, config.thisTranslation["showControlPanelOnStartup"]), ("preferControlPanelForCommandLineEntry", config.preferControlPanelForCommandLineEntry, self.preferControlPanelForCommandLineEntryChanged, False, config.thisTranslation["preferControlPanelForCommandLineEntry"]), ("closeControlPanelAfterRunningCommand", config.closeControlPanelAfterRunningCommand, self.closeControlPanelAfterRunningCommandChanged, True, config.thisTranslation["closeControlPanelAfterRunningCommand"]), ("restrictControlPanelWidth", config.restrictControlPanelWidth, self.restrictControlPanelWidthChanged, False, config.thisTranslation["restrictControlPanelWidth"]), ("clearCommandEntry", config.clearCommandEntry, self.clearCommandEntryChanged, False, config.thisTranslation["clearCommandEntry"]), ("openBibleWindowContentOnNextTab", config.openBibleWindowContentOnNextTab, self.openBibleWindowContentOnNextTabChanged, False, config.thisTranslation["openBibleWindowContentOnNextTab"]), ("openStudyWindowContentOnNextTab", config.openStudyWindowContentOnNextTab, self.openStudyWindowContentOnNextTabChanged, True, config.thisTranslation["openStudyWindowContentOnNextTab"]), ("populateTabsOnStartup", config.populateTabsOnStartup, self.populateTabsOnStartupChanged, False, config.thisTranslation["populateTabsOnStartup"]), ("qtMaterial", config.qtMaterial, self.qtMaterialChanged, False, config.thisTranslation["qtMaterial"]), ("addBreakAfterTheFirstToolBar", config.addBreakAfterTheFirstToolBar, self.addBreakAfterTheFirstToolBarChanged, True, config.thisTranslation["addBreakAfterTheFirstToolBar"]), ("addBreakBeforeTheLastToolBar", config.addBreakBeforeTheLastToolBar, self.addBreakBeforeTheLastToolBarChanged, False, config.thisTranslation["addBreakBeforeTheLastToolBar"]), ("parserStandarisation", (config.parserStandarisation == "YES"), self.parserStandarisationChanged, False, config.thisTranslation["parserStandarisation"]), ("useLiteVerseParsing", config.useLiteVerseParsing, self.useLiteVerseParsingChanged, False, config.thisTranslation["useLiteVerseParsing"]), ("parseEnglishBooksOnly", config.parseEnglishBooksOnly, self.parseEnglishBooksOnlyChanged, False, config.thisTranslation["parseEnglishBooksOnly"]), ("parseWordDocument", config.parseWordDocument, self.parseWordDocumentChanged, True, config.thisTranslation["parseWordDocument"]), ("convertChapterVerseDotSeparator", config.convertChapterVerseDotSeparator, self.convertChapterVerseDotSeparatorChanged, True, config.thisTranslation["convertChapterVerseDotSeparator"]), ("parseBookChapterWithoutSpace", config.parseBookChapterWithoutSpace, self.parseBookChapterWithoutSpaceChanged, True, config.thisTranslation["parseBookChapterWithoutSpace"]), ("parseBooklessReferences", config.parseBooklessReferences, self.parseBooklessReferencesChanged, True, config.thisTranslation["parseBooklessReferences"]), ("searchBibleIfCommandNotFound", config.searchBibleIfCommandNotFound, self.searchBibleIfCommandNotFoundChanged, True, config.thisTranslation["searchBibleIfCommandNotFound"]), ("regexSearchBibleIfCommandNotFound", config.regexSearchBibleIfCommandNotFound, self.regexSearchBibleIfCommandNotFoundChanged, False, config.thisTranslation["regexSearchBibleIfCommandNotFound"]), ("preferHtmlMenu", config.preferHtmlMenu, self.preferHtmlMenuChanged, False, config.thisTranslation["preferHtmlMenu"]), ("showVerseNumbersInRange", config.showVerseNumbersInRange, self.showVerseNumbersInRangeChanged, True, config.thisTranslation["showVerseNumbersInRange"]), ("addFavouriteToMultiRef", config.addFavouriteToMultiRef, self.addFavouriteToMultiRefChanged, False, config.thisTranslation["addFavouriteToMultiRef"]), ("enableVerseHighlighting", config.enableVerseHighlighting, self.enableVerseHighlightingChanged, True, config.thisTranslation["enableVerseHighlighting"]), ("regexCaseSensitive", config.regexCaseSensitive, self.regexCaseSensitiveChanged, False, config.thisTranslation["regexCaseSensitive"]), ("alwaysDisplayStaticMaps", config.alwaysDisplayStaticMaps, self.alwaysDisplayStaticMapsChanged, False, config.thisTranslation["alwaysDisplayStaticMaps"]), ("exportEmbeddedImages", config.exportEmbeddedImages, self.exportEmbeddedImagesChanged, True, config.thisTranslation["exportEmbeddedImages"]), ("clickToOpenImage", config.clickToOpenImage, self.clickToOpenImageChanged, True, config.thisTranslation["clickToOpenImage"]), ("showNoteIndicatorOnBibleChapter", config.showNoteIndicatorOnBibleChapter, self.parent.enableNoteIndicatorButtonClicked, True, config.thisTranslation["showNoteIndicatorOnBibleChapter"]), ("openBibleNoteAfterSave", config.openBibleNoteAfterSave, self.openBibleNoteAfterSaveChanged, False, config.thisTranslation["openBibleNoteAfterSave"]), ("openBibleNoteAfterEditorClosed", config.openBibleNoteAfterEditorClosed, self.openBibleNoteAfterEditorClosedChanged, False, config.thisTranslation["openBibleNoteAfterEditorClosed"]), ("hideNoteEditorStyleToolbar", config.hideNoteEditorStyleToolbar, self.hideNoteEditorStyleToolbarChanged, False, config.thisTranslation["hideNoteEditorStyleToolbar"]), ("hideNoteEditorTextUtility", config.hideNoteEditorTextUtility, self.hideNoteEditorTextUtilityChanged, True, config.thisTranslation["hideNoteEditorTextUtility"]), ("overwriteNoteFont", config.overwriteNoteFont, self.overwriteNoteFontChanged, True, config.thisTranslation["overwriteNoteFont"]), ("overwriteNoteFontSize", config.overwriteNoteFontSize, self.overwriteNoteFontSizeChanged, True, config.thisTranslation["overwriteNoteFontSize"]), ("overwriteBookFont", config.overwriteBookFont, self.overwriteBookFontChanged, True, config.thisTranslation["overwriteBookFont"]), ("overwriteBookFontSize", config.overwriteBookFontSize, self.overwriteBookFontSizeChanged, True, config.thisTranslation["overwriteBookFontSize"]), ("openBookInNewWindow", config.openBookInNewWindow, self.openBookInNewWindowChanged, False, config.thisTranslation["openBookInNewWindow"]), ("openPdfViewerInNewWindow", config.openPdfViewerInNewWindow, self.openPdfViewerInNewWindowChanged, False, config.thisTranslation["openPdfViewerInNewWindow"]), ("virtualKeyboard", config.virtualKeyboard, self.virtualKeyboardChanged, False, config.thisTranslation["virtualKeyboard"]), ("useWebbrowser", config.useWebbrowser, self.useWebbrowserChanged, True, config.thisTranslation["useWebbrowser"]), ("removeHighlightOnExit", config.removeHighlightOnExit, self.removeHighlightOnExitChanged, False, config.thisTranslation["removeHighlightOnExit"]), ("disableModulesUpdateCheck", config.disableModulesUpdateCheck, self.disableModulesUpdateCheckChanged, True, config.thisTranslation["disableModulesUpdateCheck"]), ("updateWithGitPull", config.updateWithGitPull, self.updateWithGitPullChanged, False, config.thisTranslation["updateWithGitPull"]), ("enableGist", config.enableGist, self.enableGistChanged, False, config.thisTranslation["enableGist"]), ("enableMacros", config.enableMacros, self.enableMacrosChanged, False, config.thisTranslation["enableMacros"]), ("enablePlugins", config.enablePlugins, self.enablePluginsChanged, True, config.thisTranslation["enablePlugins"]), ("hideBlankVerseCompare", config.hideBlankVerseCompare, self.hideBlankVerseCompareChanged, False, config.thisTranslation["hideBlankVerseCompare"]), ("enforceCompareParallel", config.enforceCompareParallel, self.parent.enforceCompareParallelButtonClicked, False, config.thisTranslation["enforceCompareParallel"]), ("enableMenuUnderline", config.enableMenuUnderline, self.enableMenuUnderlineChanged, True, config.thisTranslation["enableMenuUnderline"]), ("openBibleInMainViewOnly", config.openBibleInMainViewOnly, self.parent.enableStudyBibleButtonClicked, False, config.thisTranslation["openBibleInMainViewOnly"]), ("addOHGBiToMorphologySearch", config.addOHGBiToMorphologySearch, self.addOHGBiToMorphologySearchChanged, True, config.thisTranslation["addOHGBiToMorphologySearch"]), ("includeStrictDocTypeInNote", config.includeStrictDocTypeInNote, self.includeStrictDocTypeInNoteChanged, False, config.thisTranslation["includeStrictDocTypeInNote"]), ("parseTextConvertNotesToBook", config.parseTextConvertNotesToBook, self.parseTextConvertNotesToBookChanged, False, config.thisTranslation["parseTextConvertNotesToBook"]), ("parseTextConvertHTMLToBook", config.parseTextConvertHTMLToBook, self.parseTextConvertHTMLToBookChanged, False, config.thisTranslation["parseTextConvertHTMLToBook"]), ("displayCmdOutput", config.displayCmdOutput, self.displayCmdOutputChanged, False, config.thisTranslation["displayCmdOutput"]), ("disableLoadLastOpenFilesOnStartup", config.disableLoadLastOpenFilesOnStartup, self.disableLoadLastOpenFilesOnStartupChanged, False, config.thisTranslation["disableLoadLastOpenFilesOnStartup"]), ("disableOpenPopupWindowOnStartup", config.disableOpenPopupWindowOnStartup, self.disableOpenPopupWindowOnStartupChanged, True, config.thisTranslation["disableOpenPopupWindowOnStartup"]), ("showMiniKeyboardInMiniControl", config.showMiniKeyboardInMiniControl, self.showMiniKeyboardInMiniControlChanged, False, config.thisTranslation["showMiniKeyboardInMiniControl"]), ] if config.isTtsInstalled: options += [ ("useLangDetectOnTts", config.useLangDetectOnTts, self.useLangDetectOnTtsChanged, False, config.thisTranslation["useLangDetectOnTts"]), ("ttsEnglishAlwaysUS", config.ttsEnglishAlwaysUS, self.ttsEnglishAlwaysUSChanged, False, config.thisTranslation["ttsEnglishAlwaysUS"]), ("ttsEnglishAlwaysUK", config.ttsEnglishAlwaysUK, self.ttsEnglishAlwaysUKChanged, False, config.thisTranslation["ttsEnglishAlwaysUK"]), ("ttsChineseAlwaysMandarin", config.ttsChineseAlwaysMandarin, self.ttsChineseAlwaysMandarinChanged, False, config.thisTranslation["ttsChineseAlwaysMandarin"]), ("ttsChineseAlwaysCantonese", config.ttsChineseAlwaysCantonese, self.ttsChineseAlwaysCantoneseChanged, False, config.thisTranslation["ttsChineseAlwaysCantonese"]), ] if platform.system() == "Linux": options += [ ("linuxStartFullScreen", config.linuxStartFullScreen, self.linuxStartFullScreenChanged, False, config.thisTranslation["linuxStartFullScreen"]), ("fcitx", config.fcitx, self.fcitxChanged, False, config.thisTranslation["fcitx"]), ("ibus", config.ibus, self.ibusChanged, False, config.thisTranslation["ibus"]), ("espeak", config.espeak, self.espeakChanged, False, config.thisTranslation["espeak"]), ] if config.developer: options += [ ("forceGenerateHtml", config.forceGenerateHtml, self.forceGenerateHtmlChanged, False, config.thisTranslation["forceGenerateHtml"]), ("enableLogging", config.enableLogging, self.enableLoggingChanged, False, config.thisTranslation["enableLogging"]), ("logCommands", config.logCommands, self.logCommandsChanged, False, config.thisTranslation["logCommands"]), ] data = {} for flag, configValue, action, default, tooltip in options: data[flag] = [configValue, default, tooltip, action] return data def restoreAllDefaults(self): for key, value in self.data.items(): code = "config.{0} = {1}".format(key, value[1]) exec(code) self.resetItems() self.displayMessage(config.thisTranslation["message_restart"]) def itemChanged(self, standardItem): flag = standardItem.text() if flag in self.data and not self.isUpdating: self.data[flag][-1]() def resetItems(self): self.isUpdating = True # Empty the model before reset self.dataViewModel.clear() # Reset self.data = self.getOptions() filterEntry = self.filterEntry.text().lower() rowCount = 0 for flag, value in self.data.items(): configValue, default, tooltip, *_ = value if filterEntry == "" or (filterEntry != "" and (filterEntry in flag.lower() or filterEntry in tooltip.lower())): # 1st column item = QStandardItem(flag) item.setToolTip(tooltip) item.setCheckable(True) item.setCheckState(Qt.CheckState.Checked if configValue else Qt.CheckState.Unchecked) self.dataViewModel.setItem(rowCount, 0, item) # 2nd column item = QStandardItem(str(default)) self.dataViewModel.setItem(rowCount, 1, item) # 3rd column tooltip = tooltip.replace("\n", " ") item = QStandardItem(tooltip) item.setToolTip(tooltip) self.dataViewModel.setItem(rowCount, 2, item) # add row count rowCount += 1 self.dataViewModel.setHorizontalHeaderLabels([ config.thisTranslation["flag"], config.thisTranslation["default"], config.thisTranslation["description"] ]) self.dataView.resizeColumnsToContents() self.isUpdating = False def displayMessage(self, message="", title="UniqueBible"): QMessageBox.information(self, title, message) def openWiki(self, event): wikiLink = "https://github.com/eliranwong/UniqueBible/wiki/Config-file-reference" webbrowser.open(wikiLink) def ibusChanged(self): config.ibus = not config.ibus if config.fcitx and config.ibus: config.fcitx = not config.fcitx if config.virtualKeyboard and config.ibus: config.virtualKeyboard = not config.virtualKeyboard self.displayMessage(config.thisTranslation["message_restart"]) def fcitxChanged(self): config.fcitx = not config.fcitx if config.fcitx and config.ibus: config.ibus = not config.ibus if config.fcitx and config.virtualKeyboard: config.virtualKeyboard = not config.virtualKeyboard self.displayMessage(config.thisTranslation["message_restart"]) def virtualKeyboardChanged(self): config.virtualKeyboard = not config.virtualKeyboard if config.fcitx and config.virtualKeyboard: config.fcitx = not config.fcitx if config.virtualKeyboard and config.ibus: config.ibus = not config.ibus self.displayMessage(config.thisTranslation["message_restart"]) def parseWordDocumentChanged(self): config.parseWordDocument = not config.parseWordDocument def useLangDetectOnTtsChanged(self): config.useLangDetectOnTts = not config.useLangDetectOnTts def ttsEnglishAlwaysUSChanged(self): config.ttsEnglishAlwaysUS = not config.ttsEnglishAlwaysUS if config.ttsEnglishAlwaysUK and config.ttsEnglishAlwaysUS: config.ttsEnglishAlwaysUK = not config.ttsEnglishAlwaysUK def ttsEnglishAlwaysUKChanged(self): config.ttsEnglishAlwaysUK = not config.ttsEnglishAlwaysUK if config.ttsEnglishAlwaysUK and config.ttsEnglishAlwaysUS: config.ttsEnglishAlwaysUS = not config.ttsEnglishAlwaysUS def ttsChineseAlwaysMandarinChanged(self): config.ttsChineseAlwaysMandarin = not config.ttsChineseAlwaysMandarin if config.ttsChineseAlwaysMandarin and config.ttsChineseAlwaysCantonese: config.ttsChineseAlwaysCantonese = not config.ttsChineseAlwaysCantonese def ttsChineseAlwaysCantoneseChanged(self): config.ttsChineseAlwaysCantonese = not config.ttsChineseAlwaysCantonese if config.ttsChineseAlwaysMandarin and config.ttsChineseAlwaysCantonese: config.ttsChineseAlwaysMandarin = not config.ttsChineseAlwaysMandarin def showVerseNumbersInRangeChanged(self): config.showVerseNumbersInRange = not config.showVerseNumbersInRange #def customPythonOnStartupChanged(self): # config.customPythonOnStartup = not config.customPythonOnStartup def openBibleWindowContentOnNextTabChanged(self): config.openBibleWindowContentOnNextTab = not config.openBibleWindowContentOnNextTab self.newTabException = False def showControlPanelOnStartupChanged(self): config.showControlPanelOnStartup = not config.showControlPanelOnStartup self.displayMessage(config.thisTranslation["message_restart"]) def preferControlPanelForCommandLineEntryChanged(self): config.preferControlPanelForCommandLineEntry = not config.preferControlPanelForCommandLineEntry self.displayMessage(config.thisTranslation["message_restart"]) def closeControlPanelAfterRunningCommandChanged(self): config.closeControlPanelAfterRunningCommand = not config.closeControlPanelAfterRunningCommand def restrictControlPanelWidthChanged(self): config.restrictControlPanelWidth = not config.restrictControlPanelWidth self.parent.reloadControlPanel(False) def regexCaseSensitiveChanged(self): config.regexCaseSensitive = not config.regexCaseSensitive def openStudyWindowContentOnNextTabChanged(self): config.openStudyWindowContentOnNextTab = not config.openStudyWindowContentOnNextTab self.newTabException = False def addFavouriteToMultiRefChanged(self): config.addFavouriteToMultiRef = not config.addFavouriteToMultiRef def addOHGBiToMorphologySearchChanged(self): config.addOHGBiToMorphologySearch = not config.addOHGBiToMorphologySearch def exportEmbeddedImagesChanged(self): config.exportEmbeddedImages = not config.exportEmbeddedImages def clickToOpenImageChanged(self): config.clickToOpenImage = not config.clickToOpenImage def openBibleNoteAfterEditorClosedChanged(self): config.openBibleNoteAfterEditorClosed = not config.openBibleNoteAfterEditorClosed def preferHtmlMenuChanged(self): config.preferHtmlMenu = not config.preferHtmlMenu def hideNoteEditorStyleToolbarChanged(self): config.hideNoteEditorStyleToolbar = not config.hideNoteEditorStyleToolbar def hideNoteEditorTextUtilityChanged(self): config.hideNoteEditorTextUtility = not config.hideNoteEditorTextUtility def populateTabsOnStartupChanged(self): config.populateTabsOnStartup = not config.populateTabsOnStartup def openBookInNewWindowChanged(self): config.openBookInNewWindow = not config.openBookInNewWindow def convertChapterVerseDotSeparatorChanged(self): config.convertChapterVerseDotSeparator = not config.convertChapterVerseDotSeparator def updateWithGitPullChanged(self): config.updateWithGitPull = not config.updateWithGitPull if config.updateWithGitPull and not os.path.isdir(".git"): config.updateWithGitPull = False def parseBookChapterWithoutSpaceChanged(self): config.parseBookChapterWithoutSpace = not config.parseBookChapterWithoutSpace def parseBooklessReferencesChanged(self): config.parseBooklessReferences = not config.parseBooklessReferences def openPdfViewerInNewWindowChanged(self): config.openPdfViewerInNewWindow = not config.openPdfViewerInNewWindow def searchBibleIfCommandNotFoundChanged(self): config.searchBibleIfCommandNotFound = not config.searchBibleIfCommandNotFound def regexSearchBibleIfCommandNotFoundChanged(self): config.regexSearchBibleIfCommandNotFound = not config.regexSearchBibleIfCommandNotFound if config.regexSearchBibleIfCommandNotFound and not config.searchBibleIfCommandNotFound: config.searchBibleIfCommandNotFound = True def overwriteNoteFontChanged(self): config.overwriteNoteFont = not config.overwriteNoteFont def overwriteNoteFontSizeChanged(self): config.overwriteNoteFontSize = not config.overwriteNoteFontSize def overwriteBookFontChanged(self): config.overwriteBookFont = not config.overwriteBookFont def useWebbrowserChanged(self): config.useWebbrowser = not config.useWebbrowser def removeHighlightOnExitChanged(self): config.removeHighlightOnExit = not config.removeHighlightOnExit def overwriteBookFontSizeChanged(self): config.overwriteBookFontSize = not config.overwriteBookFontSize def alwaysDisplayStaticMapsChanged(self): config.alwaysDisplayStaticMaps = not config.alwaysDisplayStaticMaps def openBibleNoteAfterSaveChanged(self): config.openBibleNoteAfterSave = not config.openBibleNoteAfterSave def addBreakAfterTheFirstToolBarChanged(self): config.addBreakAfterTheFirstToolBar = not config.addBreakAfterTheFirstToolBar self.displayMessage(config.thisTranslation["message_restart"]) def addBreakBeforeTheLastToolBarChanged(self): config.addBreakBeforeTheLastToolBar = not config.addBreakBeforeTheLastToolBar self.displayMessage(config.thisTranslation["message_restart"]) def disableModulesUpdateCheckChanged(self): config.disableModulesUpdateCheck = not config.disableModulesUpdateCheck def forceGenerateHtmlChanged(self): config.forceGenerateHtml = not config.forceGenerateHtml def parserStandarisationChanged(self): if config.parserStandarisation == "YES": config.parserStandarisation = "NO" else: config.parserStandarisation = "YES" def linuxStartFullScreenChanged(self): config.linuxStartFullScreen = not config.linuxStartFullScreen self.displayMessage(config.thisTranslation["message_restart"]) def espeakChanged(self): config.espeak = not config.espeak self.displayMessage(config.thisTranslation["message_restart"]) def enableLoggingChanged(self): config.enableLogging = not config.enableLogging self.displayMessage(config.thisTranslation["message_restart"]) def logCommandsChanged(self): config.logCommands = not config.logCommands def enableVerseHighlightingChanged(self): config.enableVerseHighlighting = not config.enableVerseHighlighting self.displayMessage(config.thisTranslation["message_restart"]) def useLiteVerseParsingChanged(self): config.useLiteVerseParsing = not config.useLiteVerseParsing def parseEnglishBooksOnlyChanged(self): config.parseEnglishBooksOnly = not config.parseEnglishBooksOnly def enableMacrosChanged(self): config.enableMacros = not config.enableMacros self.displayMessage(config.thisTranslation["message_restart"]) def enablePluginsChanged(self): config.enablePlugins = not config.enablePlugins self.parent.setMenuLayout(config.menuLayout) def clearCommandEntryChanged(self): config.clearCommandEntry = not config.clearCommandEntry def qtMaterialChanged(self): if not config.qtMaterial: self.parent.enableQtMaterial(True) else: self.parent.enableQtMaterial(False) def enableGistChanged(self): if not config.enableGist and config.isPygithubInstalled: config.enableGist = True self.displayMessage(config.thisTranslation["message_restart"]) elif config.enableGist: config.enableGist = not config.enableGist self.displayMessage(config.thisTranslation["message_restart"]) else: self.displayMessage(config.thisTranslation["message_noSupport"]) def hideBlankVerseCompareChanged(self): config.hideBlankVerseCompare = not config.hideBlankVerseCompare def enableMenuUnderlineChanged(self): config.enableMenuUnderline = not config.enableMenuUnderline if config.enableMenuUnderline: config.menuUnderline = "&" else: config.menuUnderline = "" self.parent.setMenuLayout(config.menuLayout) def includeStrictDocTypeInNoteChanged(self): config.includeStrictDocTypeInNote = not config.includeStrictDocTypeInNote def parseTextConvertNotesToBookChanged(self): config.parseTextConvertNotesToBook = not config.parseTextConvertNotesToBook def parseTextConvertHTMLToBookChanged(self): config.parseTextConvertHTMLToBook = not config.parseTextConvertHTMLToBook def displayCmdOutputChanged(self): config.displayCmdOutput = not config.displayCmdOutput def disableLoadLastOpenFilesOnStartupChanged(self): config.disableLoadLastOpenFilesOnStartup = not config.disableLoadLastOpenFilesOnStartup def disableOpenPopupWindowOnStartupChanged(self): config.disableOpenPopupWindowOnStartup = not config.disableOpenPopupWindowOnStartup def showMiniKeyboardInMiniControlChanged(self): config.showMiniKeyboardInMiniControl = not config.showMiniKeyboardInMiniControl
class BibleCollectionDialog(QDialog): def __init__(self, parent): super().__init__() self.setWindowTitle(config.thisTranslation["bibleCollections"]) self.setMinimumSize(680, 500) self.selectedCollection = None self.settingBibles = False self.bibles = self.getBibles() self.setupUI() self.parent = parent def setupUI(self): mainLayout = QVBoxLayout() title = QLabel(config.thisTranslation["bibleCollections"]) mainLayout.addWidget(title) self.collectionsLayout = QVBoxLayout() self.collectionsList = QListWidget() self.collectionsList.setMaximumHeight(90) self.collectionsLayout.addWidget(self.collectionsList) mainLayout.addLayout(self.collectionsLayout) self.showListOfCollections() buttonsLayout = QHBoxLayout() addButton = QPushButton(config.thisTranslation["add"]) addButton.clicked.connect(self.addNewCollection) buttonsLayout.addWidget(addButton) removeButton = QPushButton(config.thisTranslation["remove"]) removeButton.clicked.connect(self.removeCollection) buttonsLayout.addWidget(removeButton) renameButton = QPushButton(config.thisTranslation["rename"]) renameButton.clicked.connect(self.renameCollection) buttonsLayout.addWidget(renameButton) buttonsLayout.addStretch() mainLayout.addLayout(buttonsLayout) self.biblesTable = QTableView() self.biblesTable.setEnabled(False) self.biblesTable.setEditTriggers(QAbstractItemView.NoEditTriggers) self.biblesTable.setSortingEnabled(True) self.dataViewModel = QStandardItemModel(self.biblesTable) self.biblesTable.setModel(self.dataViewModel) self.loadBibleSelection() self.dataViewModel.itemChanged.connect(self.bibleSelectionChanged) mainLayout.addWidget(self.biblesTable) buttonLayout = QHBoxLayout() button = QPushButton(config.thisTranslation["close"]) button.clicked.connect(self.reloadControlPanel) button.clicked.connect(self.close) buttonLayout.addWidget(button) mainLayout.addLayout(buttonLayout) self.setLayout(mainLayout) def showListOfCollections(self): self.collectionsList.clear() if len(config.bibleCollections) > 0: for collection in sorted(config.bibleCollections.keys()): showBibleSelection = QRadioButton() showBibleSelection.setChecked(False) self.collectionsList.itemClicked.connect(self.selectCollection) self.collectionsList.addItem(collection) else: self.collectionsList.addItem("[No collection defined]") def addNewCollection(self): name, ok = QInputDialog.getText(self, 'Collection', 'Collection name:') if ok and len(name) > 0 and name != "All": config.bibleCollections[name] = {} self.showListOfCollections() self.biblesTable.setEnabled(False) def removeCollection(self): config.bibleCollections.pop(self.selectedCollection, None) self.showListOfCollections() self.biblesTable.setEnabled(False) def renameCollection(self): name, ok = QInputDialog.getText(self, 'Collection', 'Collection name:', text=self.selectedCollection) if ok and len(name) > 0 and name != "All": biblesInCollection = config.bibleCollections[ self.selectedCollection] config.bibleCollections.pop(self.selectedCollection, None) self.selectedCollection = name config.bibleCollections[name] = biblesInCollection self.showListOfCollections() self.biblesTable.setEnabled(False) def getBibles(self): from db.BiblesSqlite import BiblesSqlite from db.BiblesSqlite import Bible bibles = BiblesSqlite().getBibleList() bibleInfo = [] for bible in bibles: description = Bible(bible).bibleInfo() bibleInfo.append((bible, description)) return bibleInfo def selectCollection(self, item): self.selectedCollection = item.text() self.biblesTable.setEnabled(True) self.loadBibleSelection() def bibleSelectionChanged(self, item): if not self.settingBibles: if self.selectedCollection is not None: text = item.text() biblesInCollection = config.bibleCollections[ self.selectedCollection] if len(biblesInCollection) == 0: biblesInCollection = [] if text in biblesInCollection: biblesInCollection.remove(text) else: biblesInCollection.append(text) config.bibleCollections[ self.selectedCollection] = biblesInCollection def loadBibleSelection(self): self.settingBibles = True self.dataViewModel.clear() biblesInCollection = [] if self.selectedCollection is not None: biblesInCollection = config.bibleCollections[ self.selectedCollection] rowCount = 0 for bible, description in self.bibles: item = QStandardItem(bible) item.setToolTip(bible) item.setCheckable(True) if bible in biblesInCollection: item.setCheckState(Qt.Checked) self.dataViewModel.setItem(rowCount, 0, item) item = QStandardItem(description) self.dataViewModel.setItem(rowCount, 1, item) rowCount += 1 self.dataViewModel.setHorizontalHeaderLabels([ config.thisTranslation["bible"], config.thisTranslation["description"] ]) self.biblesTable.resizeColumnsToContents() self.settingBibles = False def reloadControlPanel(self): self.parent.reloadControlPanel(False)