示例#1
0
 def commentaryListView(self):
     # https://doc.qt.io/archives/qtforpython-5.12/PySide2/QtCore/QStringListModel.html
     # https://gist.github.com/minoue/9f384cd36339429eb0bf
     # https://www.pythoncentral.io/pyside-pyqt-tutorial-qlistview-and-qstandarditemmodel/
     list = QListView()
     list.setEditTriggers(QAbstractItemView.NoEditTriggers)
     model = QStandardItemModel(list)
     for index, commentary in enumerate(self.parent.commentaryFullNameList):
         item = QStandardItem(commentary)
         item.setToolTip(self.parent.commentaryList[index])
         #item.setCheckable(True)
         #item.setCheckState(Qt.CheckState.Checked)
         #item.setCheckState(Qt.CheckState.Unchecked)
         #print(item.checkState() is Qt.CheckState.Checked)
         model.appendRow(item)
     #model = QStringListModel(self.parent.commentaryList)
     #model = QStringListModel(self.parent.commentaryFullNameList)
     list.setModel(model)
     if config.commentaryText in self.parent.commentaryList:
         list.setCurrentIndex(
             model.index(
                 self.parent.commentaryList.index(config.commentaryText),
                 0))
     list.selectionModel().selectionChanged.connect(self.commentarySelected)
     return list
示例#2
0
 def pdfListView(self):
     list = QListView()
     list.setEditTriggers(QAbstractItemView.NoEditTriggers)
     model = QStandardItemModel(list)
     for pdf in self.pdfList:
         item = QStandardItem(pdf)
         model.appendRow(item)
     list.setModel(model)
     if config.pdfText in self.parent.pdfList:
         list.setCurrentIndex(
             model.index(self.parent.pdfList.index(config.pdfText), 0))
     list.selectionModel().selectionChanged.connect(self.pdfSelected)
     return list
示例#3
0
class Switcher(QDialog):
    """
    A multi purpose switcher.

    Example
    -------
      SwitcherItem:      [title description    <shortcut> section]
      SwitcherItem:      [title description    <shortcut> section]
      SwitcherSeparator: [---------------------------------------]
      SwitcherItem:      [title description    <shortcut> section]
      SwitcherItem:      [title description    <shortcut> section]
    """

    # Dismissed switcher
    sig_rejected = Signal()
    # Search/Filter text changes
    sig_text_changed = Signal(TEXT_TYPES[-1])
    # Current item changed
    sig_item_changed = Signal(object)
    # List item selected, mode and cleaned search text
    sig_item_selected = Signal(
        object,
        TEXT_TYPES[-1],
        TEXT_TYPES[-1],
    )
    sig_mode_selected = Signal(TEXT_TYPES[-1])

    _MAX_NUM_ITEMS = 15
    _MIN_WIDTH = 580
    _MIN_HEIGHT = 200
    _MAX_HEIGHT = 390
    _ITEM_WIDTH = _MIN_WIDTH - 20

    def __init__(self,
                 parent,
                 help_text=None,
                 item_styles=ITEM_STYLES,
                 item_separator_styles=ITEM_SEPARATOR_STYLES):
        """Multi purpose switcher."""
        super(Switcher, self).__init__(parent)
        self._visible_rows = 0
        self._modes = {}
        self._mode_on = ''
        self._item_styles = item_styles
        self._item_separator_styles = item_separator_styles

        # Widgets
        self.edit = QLineEdit(self)
        self.list = QListView(self)
        self.model = QStandardItemModel(self.list)
        self.proxy = SwitcherProxyModel(self.list)
        self.filter = KeyPressFilter()

        # Widgets setup
        self.setWindowFlags(Qt.Popup | Qt.FramelessWindowHint)
        self.setWindowOpacity(0.95)
        #        self.setMinimumHeight(self._MIN_HEIGHT)
        self.setMaximumHeight(self._MAX_HEIGHT)
        self.edit.installEventFilter(self.filter)
        self.edit.setPlaceholderText(help_text if help_text else '')
        self.list.setMinimumWidth(self._MIN_WIDTH)
        self.list.setItemDelegate(HTMLDelegate(self))
        self.list.setFocusPolicy(Qt.NoFocus)
        self.list.setSelectionBehavior(self.list.SelectItems)
        self.list.setSelectionMode(self.list.SingleSelection)
        self.list.setVerticalScrollMode(QAbstractItemView.ScrollPerItem)
        self.proxy.setSourceModel(self.model)
        self.list.setModel(self.proxy)

        # Layout
        layout = QVBoxLayout()
        layout.addWidget(self.edit)
        layout.addWidget(self.list)
        self.setLayout(layout)

        # Signals
        self.filter.sig_up_key_pressed.connect(self.previous_row)
        self.filter.sig_down_key_pressed.connect(self.next_row)
        self.filter.sig_enter_key_pressed.connect(self.enter)
        self.edit.textChanged.connect(self.setup)
        self.edit.textChanged.connect(self.sig_text_changed)
        self.edit.returnPressed.connect(self.enter)
        self.list.clicked.connect(self.enter)
        self.list.clicked.connect(self.edit.setFocus)
        self.list.selectionModel().currentChanged.connect(
            self.current_item_changed)
        self.edit.setFocus()

    # --- Helper methods
    def _add_item(self, item, last_item=True):
        """Perform common actions when adding items."""
        item.set_width(self._ITEM_WIDTH)
        self.model.appendRow(item)
        self._visible_rows = self.model.rowCount()
        if last_item:
            # Only set the current row to the first item when the added item is
            # the last one in order to prevent performance issues when
            # adding multiple items
            self.set_current_row(0)
            self.set_height()
        self.setup_sections()

    # --- API
    def clear(self):
        """Remove all items from the list and clear the search text."""
        self.set_placeholder_text('')
        self.model.beginResetModel()
        self.model.clear()
        self.model.endResetModel()
        self.setMinimumHeight(self._MIN_HEIGHT)

    def set_placeholder_text(self, text):
        """Set the text appearing on the empty line edit."""
        self.edit.setPlaceholderText(text)

    def add_mode(self, token, description):
        """Add mode by token key and description."""
        if len(token) == 1:
            self._modes[token] = description
        else:
            raise Exception('Token must be of length 1!')

    def get_mode(self):
        """Get the current mode the switcher is in."""
        return self._mode_on

    def remove_mode(self, token):
        """Remove mode by token key."""
        if token in self._modes:
            self._modes.pop(token)

    def clear_modes(self):
        """Delete all modes spreviously defined."""
        del self._modes
        self._modes = {}

    def add_item(self,
                 icon=None,
                 title=None,
                 description=None,
                 shortcut=None,
                 section=None,
                 data=None,
                 tool_tip=None,
                 action_item=False,
                 last_item=True):
        """Add switcher list item."""
        item = SwitcherItem(parent=self.list,
                            icon=icon,
                            title=title,
                            description=description,
                            data=data,
                            shortcut=shortcut,
                            section=section,
                            action_item=action_item,
                            tool_tip=tool_tip,
                            styles=self._item_styles)
        self._add_item(item, last_item=last_item)

    def add_separator(self):
        """Add separator item."""
        item = SwitcherSeparatorItem(parent=self.list,
                                     styles=self._item_separator_styles)
        self._add_item(item)

    def setup(self):
        """Set-up list widget content based on the filtering."""
        # Check exited mode
        mode = self._mode_on
        if mode:
            search_text = self.search_text()[len(mode):]
        else:
            search_text = self.search_text()

        # Check exited mode
        if self.search_text() == '':
            self._mode_on = ''
            self.clear()
            self.sig_mode_selected.emit(self._mode_on)
            return

        # Check entered mode
        for key in self._modes:
            if self.search_text().startswith(key) and not mode:
                self._mode_on = key
                self.sig_mode_selected.emit(key)
                return

        # Filter by text
        titles = []
        for row in range(self.model.rowCount()):
            item = self.model.item(row)
            if isinstance(item, SwitcherItem):
                title = item.get_title()
            else:
                title = ''
            titles.append(title)

        search_text = clean_string(search_text)
        scores = get_search_scores(to_text_string(search_text),
                                   titles,
                                   template=u"<b>{0}</b>")

        self._visible_rows = self.model.rowCount()
        for idx, score in enumerate(scores):
            title, rich_title, score_value = score
            item = self.model.item(idx)

            if not self._is_separator(item) and not item.is_action_item():
                rich_title = rich_title.replace(" ", "&nbsp;")
                item.set_rich_title(rich_title)

            item.set_score(score_value)
            proxy_index = self.proxy.mapFromSource(self.model.index(idx, 0))

            if not item.is_action_item():
                self.list.setRowHidden(proxy_index.row(), score_value == -1)

                if score_value == -1:
                    self._visible_rows -= 1

        if self._visible_rows:
            self.set_current_row(0)
        else:
            self.set_current_row(-1)

        self.setup_sections()
        self.set_height()

    def setup_sections(self):
        """Set-up which sections appear on the item list."""
        mode = self._mode_on
        if mode:
            search_text = self.search_text()[len(mode):]
        else:
            search_text = self.search_text()

        if search_text:
            for row in range(self.model.rowCount()):
                item = self.model.item(row)
                if isinstance(item, SwitcherItem):
                    item.set_section_visible(False)
        else:
            sections = []
            for row in range(self.model.rowCount()):
                item = self.model.item(row)
                if isinstance(item, SwitcherItem):
                    sections.append(item.get_section())
                    item.set_section_visible(bool(search_text))
                else:
                    sections.append('')

                if row != 0:
                    visible = sections[row] != sections[row - 1]
                    if not self._is_separator(item):
                        item.set_section_visible(visible)
                else:
                    item.set_section_visible(True)

        self.proxy.sortBy('_score')
        self.sig_item_changed.emit(self.current_item())

    def set_height(self):
        """Set height taking into account the number of items."""
        if self._visible_rows >= self._MAX_NUM_ITEMS:
            switcher_height = self._MAX_HEIGHT
        elif self._visible_rows != 0 and self.current_item():
            current_item = self.current_item()
            item_height = current_item.get_height()
            list_height = item_height * (self._visible_rows + 3)
            edit_height = self.edit.height()
            spacing_height = self.layout().spacing() * 4
            switcher_height = list_height + edit_height + spacing_height
            switcher_height = max(switcher_height, self._MIN_HEIGHT)
        else:
            switcher_height = self._MIN_HEIGHT
        self.setFixedHeight(switcher_height)

    def set_position(self, top):
        """Set the position of the dialog."""
        parent = self.parent()
        if parent is not None:
            geo = parent.geometry()
            width = self.list.width()  # This has been set in setup
            left = parent.geometry().width() / 2 - width / 2

            while parent:
                geo = parent.geometry()
                top += geo.top()
                left += geo.left()
                parent = parent.parent()

            self.move(round(left), top)

    @Slot(QModelIndex, QModelIndex)
    def current_item_changed(self, current, previous):
        """Handle item selection."""
        self.sig_item_changed.emit(self.current_item())

    # --- Qt overrides
    # ------------------------------------------------------------------------
    @Slot()
    @Slot(QListWidgetItem)
    def enter(self, itemClicked=None):
        """Override Qt method."""
        row = self.current_row()
        model_index = self.proxy.mapToSource(self.proxy.index(row, 0))
        item = self.model.item(model_index.row())
        if item:
            mode = self._mode_on
            self.sig_item_selected.emit(item, mode,
                                        self.search_text()[len(mode):])

    def accept(self):
        """Override Qt method."""
        super(Switcher, self).accept()

    def reject(self):
        """Override Qt method."""
        self.set_search_text('')
        self.sig_rejected.emit()
        super(Switcher, self).reject()

    def resizeEvent(self, event):
        """Override Qt method."""
        super(Switcher, self).resizeEvent(event)

    # --- Helper methods: Lineedit widget
    def search_text(self):
        """Get the normalized (lowecase) content of the search text."""
        return to_text_string(self.edit.text()).lower()

    def set_search_text(self, string):
        """Set the content of the search text."""
        self.edit.setText(string)

    # --- Helper methods: List widget
    def _is_separator(self, item):
        """Check if item is an separator item (SwitcherSeparatorItem)."""
        return isinstance(item, SwitcherSeparatorItem)

    def _select_row(self, steps):
        """Select row in list widget based on a number of steps with direction.

        Steps can be positive (next rows) or negative (previous rows).
        """
        row = self.current_row() + steps
        if 0 <= row < self.count():
            self.set_current_row(row)

    def count(self):
        """Get the item count in the list widget."""
        return self._visible_rows

    def current_row(self):
        """Return the current selected row in the list widget."""
        return self.list.currentIndex().row()

    def current_item(self):
        """Return the current selected item in the list widget."""
        row = self.current_row()
        model_index = self.proxy.mapToSource(self.proxy.index(row, 0))
        item = self.model.item(model_index.row())
        return item

    def set_current_row(self, row):
        """Set the current selected row in the list widget."""
        index = self.model.index(row, 0)
        selection_model = self.list.selectionModel()

        # https://doc.qt.io/qt-5/qitemselectionmodel.html#SelectionFlag-enum
        selection_model.setCurrentIndex(index, selection_model.ClearAndSelect)

        # Ensure that the selected item is visible
        proxy_index = self.proxy.mapFromSource(index)
        self.list.scrollTo(proxy_index, QAbstractItemView.EnsureVisible)

    def previous_row(self):
        """Select previous row in list widget."""
        steps = 1
        prev_row = self.current_row() - steps

        if prev_row == -1:
            self.set_current_row(self.count() - 1)
        else:
            if prev_row >= 0:
                # Need to map the filtered list to the actual model items
                list_index = self.proxy.index(prev_row, 0)
                model_index = self.proxy.mapToSource(list_index)
                item = self.model.item(model_index.row(), 0)
                if self._is_separator(item):
                    steps += 1
            self._select_row(-steps)

    def next_row(self):
        """Select next row in list widget."""
        steps = 1
        next_row = self.current_row() + steps

        # Need to map the filtered list to the actual model items
        list_index = self.proxy.index(next_row, 0)
        model_index = self.proxy.mapToSource(list_index)
        item = self.model.item(model_index.row(), 0)

        if next_row >= self.count():
            self.set_current_row(0)
        else:
            if item:
                if self._is_separator(item):
                    steps += 1
            self._select_row(steps)
示例#4
0
class BibleReadingPlan(QWidget):

    template = {
        # Source / credits of the following plan: https://www.biblica.com/resources/reading-plans/
        1: [False, "Genesis 1, Genesis 2:1-17, Matthew 1:1-25, Psalm 1:1-6"],
        2: [
            False,
            "Genesis 2:18-25, Genesis 3, Genesis 4:1-16, Matthew 2:1-18, Psalm 2:1-12"
        ],
        3: [
            False,
            "Genesis 4:17-26, Genesis 5, Genesis 6, Matthew 2:19-23, Matthew 3, Psalm 3:1-8"
        ],
        4: [
            False,
            "Genesis 7, Genesis 8, Genesis 9:1-17, Matthew 4:1-22, Proverbs 1:1-7"
        ],
        5: [
            False,
            "Genesis 9:18-29, Genesis 10, Genesis 11:1-9, Matthew 4:23-25, Matthew 5:1-20, Psalm 4:1-8"
        ],
        6: [
            False,
            "Genesis 11:10-32, Genesis 12, Genesis 13, Matthew 5:21-42, Psalm 5:1-12"
        ],
        7: [
            False,
            "Genesis 14, Genesis 15, Genesis 16, Matthew 5:43-48, Matthew 6:1-24, Psalm 6"
        ],
        8: [
            False,
            "Genesis 17, Genesis 18, Matthew 6:25-34, Matthew 7:1-23, Proverbs 1:8-19"
        ],
        9: [
            False,
            "Genesis 19, Genesis 20:1-18, Matthew 7:24-29, Matthew 8:1-22, Psalm 7:1-9"
        ],
        10: [
            False,
            "Genesis 21, Genesis 22, Genesis 23, Matthew 8:23-34, Matthew 9:1-13, Psalm 7:10-17"
        ],
        11: [False, "Genesis 24:1-67, Matthew 9:14-38, Psalm 8:1-9"],
        12:
        [False, "Genesis 25, Genesis 26, Matthew 10:1-31, Proverbs 1:20-33"],
        13: [
            False,
            "Genesis 27, Genesis 28:1-22, Matthew 10:32-42, Matthew 11:1-15, Psalm 9:1-6"
        ],
        14: [False, "Genesis 29, Genesis 30, Matthew 11:16-30, Psalm 9:7-12"],
        15: [False, "Genesis 31:1-55, Matthew 12:1-21, Psalm 9:13-20"],
        16:
        [False, "Genesis 32, Genesis 33, Matthew 12:22-45, Proverbs 2:1-11"],
        17: [
            False,
            "Genesis 34, Genesis 35, Matthew 12:46-50, Matthew 13:1-17, Psalm 10:1-11"
        ],
        18:
        [False, "Genesis 36, Genesis 37, Matthew 13:18-35, Psalm 10:12-18"],
        19: [False, "Genesis 38, Genesis 39, Matthew 13:36-58, Psalm 11:1-7"],
        20: [
            False,
            "Genesis 40, Genesis 41:1-40, Matthew 14:1-21, Proverbs 2:12-22"
        ],
        21: [
            False,
            "Genesis 41:41-57, Genesis 42, Matthew 14:22-36, Matthew 15:1-9, Psalm 12:1-8"
        ],
        22: [False, "Genesis 43, Genesis 44, Matthew 15:10-39, Psalm 13:1-6"],
        23: [
            False,
            "Genesis 45, Genesis 46, Genesis 47:1-12, Matthew 16:1-20, Psalm 14:1-7"
        ],
        24: [
            False,
            "Genesis 47:13-31, Genesis 48, Matthew 16:21-28, Matthew 17:1-13, Proverbs 3:1-10"
        ],
        25: [
            False,
            "Genesis 49, Genesis 50, Matthew 17:14-27, Matthew 18:1-9, Psalm 15:1-5"
        ],
        26: [False, "Job 1, Job 2, Job 3, Matthew 18:10-35, Psalm 16:1-11"],
        27:
        [False, "Job 4, Job 5, Job 6, Job 7, Matthew 19:1-15, Psalm 17:1-5"],
        28:
        [False, "Job 8, Job 9, Job 10, Matthew 19:16-30, Proverbs 3:11-20"],
        29: [
            False,
            "Job 11, Job 12, Job 13, Job 14, Matthew 20:1-19, Psalm 17:6-12"
        ],
        30: [
            False,
            "Job 15, Job 16, Job 17, Job 18, Matthew 20:20-34, Psalm 17:13-15"
        ],
        31: [False, "Job 19, Job 20, Job 21, Matthew 21:1-17, Psalm 18:1-6"],
        32:
        [False, "Job 22, Job 23, Job 24, Matthew 21:18-32, Proverbs 3:21-35"],
        33: [
            False,
            "Job 25, Job 26, Job 27, Job 28, Job 29, Matthew 21:33-46, Matthew 22:1-14, Psalm 18:7-15"
        ],
        34:
        [False, "Job 30, Job 31, Job 32, Matthew 22:15-46, Psalm 18:16-24"],
        35: [False, "Job 33, Job 34, Matthew 23:1-39, Psalm 18:25-36"],
        36: [False, "Job 35, Job 36, Job 37, Matthew 24:1-31, Proverbs 4:1-9"],
        37: [
            False,
            "Job 38, Job 39, Job 40:1-2, Matthew 24:32-51, Matthew 25:1-13, Psalm 18:37-42"
        ],
        38: [
            False,
            "Job 40:3-24, Job 41, Job 42, Matthew 25:14-46, Psalm 18:43-50"
        ],
        39:
        [False, "Exodus 1, Exodus 2, Exodus 3, Matthew 26:1-30, Psalm 19:1-6"],
        40: [
            False,
            "Exodus 4, Exodus 5, Exodus 6:1-12, Matthew 26:31-46, Proverbs 4:10-19"
        ],
        41: [
            False,
            "Exodus 6:13-30, Exodus 7,Exodus 8, Matthew 26:47-68, Psalm 19:7-14"
        ],
        42: [
            False,
            "Exodus 9, Exodus 10, Matthew 26:69-75, Matthew 27:1-10, Psalm 20:1-9"
        ],
        43: [False, "Exodus 11, Exodus 12, Matthew 27:11-44, Psalm 21:1-7"],
        44:
        [False, "Exodus 13, Exodus 14, Matthew 27:45-66, Proverbs 4:20-27"],
        45: [False, "Exodus 15, Exodus 16, Matthew 28:1-20, Psalm 21:8-13"],
        46: [False, "Exodus 17, Exodus 18, Mark 1:1-28, Psalm 22:1-11"],
        47: [
            False,
            "Exodus 19, Exodus 20, Mark 1:29-45, Mark 2:1-17, Psalm 22:12-21"
        ],
        48: [
            False,
            "Exodus 21, Exodus 22, Mark 2:18-27, Mark 3:1-30, Proverbs 5:1-14"
        ],
        49: [
            False,
            "Exodus 23, Exodus 24, Mark 3:31-35, Mark 4:1-29, Psalm 22:22-31"
        ],
        50: [
            False,
            "Exodus 25, Exodus 26, Mark 4:30-41, Mark 5:1-20, Psalm 23:1-6"
        ],
        51: [
            False,
            "Exodus 27, Exodus 28, Mark 5:21-43, Mark 6:1-6, Psalm 24:1-10"
        ],
        52: [False, "Exodus 29, Exodus 30, Mark 6:7-29, Proverbs 5:15-23"],
        53: [
            False,
            "Exodus 31, Exodus 32, Exodus 33:1-6, Mark 6:30-56, Psalm 25:1-7"
        ],
        54: [False, "Exodus 33:7-23, Exodus 34, Mark 7:1-30, Psalm 25:8-15"],
        55: [
            False,
            "Exodus 35, Exodus 36, Mark 7:31-37, Mark 8:1-13, Psalm 25:16-22"
        ],
        56: [
            False,
            "Exodus 37, Exodus 38, Mark 8:14-38, Mark 9:1, Proverbs 6:1-11"
        ],
        57: [False, "Exodus 39, Exodus 40, Mark 9:2-32, Psalm 26:1-12"],
        58: [
            False,
            "Leviticus 1, Leviticus 2, Leviticus 3, Mark 9:33-50, Mark 10:1-12, Psalm 27:1-6"
        ],
        59:
        [False, "Leviticus 4, Leviticus 5:1-13, Mark 10:13-31, Psalm 27:7-14"],
        60: [
            False,
            "Leviticus 5:14-19, Leviticus 6, Leviticus 7:1-10, Mark 10:32-52, Proverbs 6:12-19"
        ],
        61: [False, "Leviticus 7:11-38, Leviticus 8, Mark 11:1-27, Psalm 28"],
        62: [
            False,
            "Leviticus 9, Leviticus 10, Mark 11:28-33, Mark 12:1-12, Psalm 29"
        ],
        63: [False, "Leviticus 11, Leviticus 12, Mark 12:13-27, Psalm 30:1-7"],
        64: [False, "Leviticus 13:1-59, Mark 12:28-44, Proverbs 6:20-29"],
        65: [False, "Leviticus 14:1-57, Mark 13:1-31, Psalm 30:8-12"],
        66: [
            False,
            "Leviticus 15, Leviticus 16, Mark 13:32-37, Mark 14:1-16, Psalm 31:1-8"
        ],
        67:
        [False, "Leviticus 17, Leviticus 18, Mark 14:17-42, Psalm 31:9-18"],
        68:
        [False, "Leviticus 19, Leviticus 20, Mark 14:43-72, Proverbs 6:30-35"],
        69: [
            False, "Leviticus 21, Leviticus 22, Mark 15:1-32, Psalm 31:19-24"
        ],
        70: [False, "Leviticus 23, Leviticus 24, Mark 15:33-47, Psalm 32"],
        71: [
            False,
            "Leviticus 25, Leviticus 26:1-13, Mark 16:1-20, Psalm 33:1-11"
        ],
        72: [
            False,
            "Leviticus 26:14-46, Leviticus 27, Luke 1:1-25, Proverbs 7:1-5"
        ],
        73: [False, "Numbers 1, Numbers 2:1-9, Luke 1:26-38, Psalm 33:12-22"],
        74: [False, "Numbers 2:10-34, Numbers 3, Luke 1:39-56, Psalm 34:1-10"],
        75: [False, "Numbers 4, Numbers 5:1-10, Luke 1:57-80, Psalm 34:11-22"],
        76: [
            False,
            "Numbers 5:11-31, Numbers 6:1-27, Luke 2:1-20, Proverbs 7:6-20"
        ],
        77: [False, "Numbers 7:1-65, Luke 2:21-40, Psalm 35:1-10"],
        78: [
            False,
            "Numbers 7:66-89, Numbers 8, Numbers 9:1-14, Luke 2:41-52, Psalm 35:11-18"
        ],
        79: [
            False,
            "Numbers 9:15-23, Numbers 10, Numbers 11:1-3, Luke 3:1-22, Psalm 35:19-28"
        ],
        80: [
            False,
            "Numbers 11:4-35, Numbers 12, Numbers 13:1-25, Luke 3:23-38, Luke 4:1-13, Proverbs 7:21-27"
        ],
        81: [
            False, "Numbers 13:26-33, Numbers 14, Luke 4:14-37, Psalm 36:1-12"
        ],
        82: [
            False,
            "Numbers 15, Numbers 16:1-35, Luke 4:38-44, Luke 5:1-16, Psalm 37:1-9"
        ],
        83: [
            False,
            "Numbers 16:36-50, Numbers 17, Numbers 18, Luke 5:17-32, Psalm 37:10-20"
        ],
        84: [
            False,
            "Numbers 19, Numbers 20, Numbers 21:1-3, Luke 5:33-39, Luke 6:1-11, Proverbs 8:1-11"
        ],
        85: [
            False,
            "Numbers 21:4-35, Numbers 22:1-20, Luke 6:12-36, Psalm 37:21-31"
        ],
        86: [
            False,
            "Numbers 22:21-41, Numbers 23:1-26, Luke 6:37-49, Luke 7:1-10, Psalm 37:32-40"
        ],
        87: [
            False,
            "Numbers 23:27-30, Numbers 24, Numbers 25, Luke 7:11-35, Psalm 38:1-11"
        ],
        88:
        [False, "Numbers 26, Numbers 27:1-11, Luke 7:36-50, Proverbs 8:12-21"],
        89: [
            False,
            "Numbers 27:12-23, Numbers 28, Numbers 29:1-11, Luke 8:1-18, Psalm 38:12-22"
        ],
        90: [
            False,
            "Numbers 29:12-40, Numbers 30, Numbers 31:1-24, Luke 8:19-39, Psalm 39:1-13"
        ],
        91: [
            False,
            "Numbers 31:25-54, Numbers 32, Luke 8:40-56, Luke 9:1-9, Psalm 40:1-8"
        ],
        92: [False, "Numbers 33, Numbers 34, Luke 9:10-27, Proverbs 8:22-31"],
        93: [
            False, "Numbers 35, Numbers 36:1-12, Luke 9:28-56, Psalm 40:9-17"
        ],
        94: [
            False,
            "Deuteronomy 1, Deuteronomy 2:1-23, Luke 9:57-62, Luke 10:1-24, Psalm 41:1-6"
        ],
        95: [
            False,
            "Deuteronomy 2:24-37, Deuteronomy 3, Deuteronomy 4:1-14, Luke 10:25-42, Luke 11:1-4, Psalm 41:7-13"
        ],
        96: [
            False,
            "Deuteronomy 4:15-49, Deuteronomy 5, Luke 11:5-32, Proverbs 8:32-36"
        ],
        97: [
            False,
            "Deuteronomy 6, Deuteronomy 7, Deuteronomy 8, Luke 11:33-54, Psalm 42:1-6"
        ],
        98: [
            False, "Deuteronomy 9, Deuteronomy 10, Luke 12:1-34, Psalm 42:7-11"
        ],
        99:
        [False, "Deuteronomy 11, Deuteronomy 12, Luke 12:35-59, Psalm 43:1-5"],
        100: [
            False,
            "Deuteronomy 13, Deuteronomy 14, Luke 13:1-30, Proverbs 9:1-12"
        ],
        101: [
            False,
            "Deuteronomy 15, Deuteronomy 16:1-20, Luke 13:31-35, Luke 14:1-14, Psalm 44:1-12"
        ],
        102: [
            False,
            "Deuteronomy 16:21-22, Deuteronomy 17, Deuteronomy 18, Luke 14:15-35, Psalm 44:13-26"
        ],
        103: [
            False, "Deuteronomy 19, Deuteronomy 20, Luke 15:1-32, Psalm 45:1-9"
        ],
        104: [
            False,
            "Deuteronomy 21, Deuteronomy 22, Luke 16:1-18, Proverbs 9:13-18"
        ],
        105: [
            False,
            "Deuteronomy 23, Deuteronomy 24, Deuteronomy 25:1-19, Luke 16:19-31, Luke 17:1-10, Psalm 45:10-17"
        ],
        106: [
            False,
            "Deuteronomy 26, Deuteronomy 27, Deuteronomy 28:1-14, Luke 17:11-37, Psalm 46:1-11"
        ],
        107: [False, "Deuteronomy 28:15-68, Luke 18:1-30, Psalm 47:1-9"],
        108: [
            False,
            "Deuteronomy 29, Deuteronomy 30:1-10, Luke 18:31-43, Luke 19:1-10, Proverbs 10:1-10"
        ],
        109: [
            False,
            "Deuteronomy 30:11-20, Deuteronomy 31:1-29, Luke 19:11-44, Psalm 48:1-8"
        ],
        110: [
            False,
            "Deuteronomy 31:30, Deuteronomy 32, Luke 19:45-48, Luke 20:1-26, Psalm 48:9-14"
        ],
        111: [
            False,
            "Deuteronomy 33, Deuteronomy 34:1-12, Luke 20:27-47, Luke 21:1-4, Psalm 49:1-20"
        ],
        112: [False, "Joshua 1, Joshua 2, Luke 21:5-38, Proverbs 10:11-20"],
        113: [
            False,
            "Joshua 3, Joshua 4, Joshua 5:1-12, Luke 22:1-38, Psalm 50:1-15"
        ],
        114: [
            False,
            "Joshua 5:13-15, Joshua 6, Joshua 7, Luke 22:39-62, Psalm 50:16-23"
        ],
        115: [
            False,
            "Joshua 8, Joshua 9:1-15, Luke 22:63-71, Luke 23:1-25, Psalm 51:1-9"
        ],
        116: [
            False,
            "Joshua 9:16-27, Joshua 10, Luke 23:26-56, Proverbs 10:21-30"
        ],
        117: [False, "Joshua 11, Joshua 12, Luke 24:1-35, Psalm 51:10-19"],
        118: [False, "Joshua 13, Joshua 14, Luke 24:36-53, Psalm 52:1-9"],
        119: [False, "Joshua 15, Joshua 16, John 1:1-28, Psalm 53:1-6"],
        120: [
            False,
            "Joshua 17, Joshua 18, John 1:29-51, Proverbs 10:31-32, Proverbs 11:1-8"
        ],
        121: [
            False,
            "Joshua 19, Joshua 20, Joshua 21:1-19, John 2:1-25, Psalm 54:1-7"
        ],
        122: [False, "Joshua 21:20-45, Joshua 22, John 3:1-21, Psalm 55:1-11"],
        123: [False, "Joshua 23, Joshua 24, John 3:22-36, Psalm 55:12-23"],
        124: [False, "Judges 1, Judges 2:1-5, John 4:1-26, Proverbs 11:9-18"],
        125: [False, "Judges 2:6-23, Judges 3, John 4:27-42, Psalm 56:1-13"],
        126: [
            False,
            "Judges 4, Judges 5, John 4:43-54, John 5:1-15, Psalm 57:1-6"
        ],
        127: [False, "Judges 6, Judges 7:1-8, John 5:16-30, Psalm 57:7-11"],
        128: [
            False, "Judges 7:8-25, Judges 8, John 5:31-47, Proverbs 11:19-28"
        ],
        129: [False, "Judges 9, John 6:1-24, Psalm 58:1-11"],
        130: [False, "Judges 10, Judges 11, John 6:25-59, Psalm 59:1-8"],
        131: [
            False,
            "Judges 12, Judges 13, John 6:60-71, John 7:1-13, Psalm 59:9-19"
        ],
        132: [
            False,
            "Judges 14, Judges 15, John 7:14-44, Proverbs 11:29-31, Proverbs 12:1-7"
        ],
        133: [
            False,
            "Judges 16, Judges 17, John 7:45-53, John 8:1-11, Psalm 60:1-4"
        ],
        134: [False, "Judges 18, Judges 19, John 8:12-30, Psalm 60:5-12"],
        135: [False, "Judges 20, Judges 21, John 8:31-59, Psalm 61:1-8"],
        136: [False, "Ruth 1, Ruth 2, John 9:1-34, Proverbs 12:8-17"],
        137: [
            False, "Ruth 3, Ruth 4, John 9:35-41, John 10:1-21, Psalm 62:1-12"
        ],
        138: [
            False, "1 Samuel 1, 1 Samuel 2:1-26, John 10:22-42, Psalm 63:1-11"
        ],
        139: [
            False,
            "1 Samuel 2:27-36, 1 Samuel 3, 1 Samuel 4, John 11:1-44, Psalm 64:1-10"
        ],
        140: [
            False,
            "1 Samuel 5, 1 Samuel 6, 1 Samuel 7, John 11:45-57, John 12:1-11, Proverbs 12:18-27"
        ],
        141: [
            False,
            "1 Samuel 8, 1 Samuel 9, 1 Samuel 10:1-8, John 12:12-26, Psalm 65:1-13"
        ],
        142: [
            False,
            "1 Samuel 10:9-27, 1 Samuel 11, 1 Samuel 12, John 12:37-50, John 13:1-17, Psalm 66:1-12"
        ],
        143: [
            False,
            "1 Samuel 13, 1 Samuel 14:1-23, John 13:18-38, Psalm 66:13-20"
        ],
        144: [
            False,
            "1 Samuel 14:24-52, 1 Samuel 15, John 14:1-31, Proverbs 12:28, Proverbs 13:1-9"
        ],
        145: [
            False,
            "1 Samuel 16, 1 Samuel 17:1-37, John 15, John 16:1-4, Psalm 67:1-7"
        ],
        146: [
            False,
            "1 Samuel 17:38-58, 1 Samuel 18, John 16:5-33, John 17:1-5, Psalm 68:1-6"
        ],
        147: [False, "1 Samuel 19, 1 Samuel 20, John 17:6-26, Psalm 68:7-14"],
        148: [
            False,
            "1 Samuel 21, 1 Samuel 22, 1 Samuel 23, John 18:1-24, Proverbs 13:10-19"
        ],
        149: [
            False, "1 Samuel 24, 1 Samuel 25, John 18:25-40, Psalm 68:15-20"
        ],
        150: [
            False,
            "1 Samuel 26, 1 Samuel 27, 1 Samuel 28, John 19:1-27, Psalm 68:21-27"
        ],
        151: [
            False,
            "1 Samuel 29, 1 Samuel 30, 1 Samuel 31, John 19:28-42, John 20:1-9, Psalm 68:28-35"
        ],
        152: [
            False,
            "2 Samuel 1, 2 Samuel 2:1-7, John 20:10-31, Proverbs 13:20-25, Proverbs 14:1-4"
        ],
        153: [
            False,
            "2 Samuel 2:8-32, 2 Samuel 3:1-21, John 21:1-25, Psalm 69:1-12"
        ],
        154: [
            False,
            "2 Samuel 3:22-39, 2 Samuel 4, 2 Samuel 5:1-5, Acts 1:1-22, Psalm 69:13-28"
        ],
        155: [
            False,
            "2 Samuel 5:6-25, 2 Samuel 6, Acts 1:23-26, Acts 2:1-21, Psalm 69:29-36"
        ],
        156: [False, "2 Samuel 7, 2 Samuel 8, Acts 2:22-47, Proverbs 14:4-14"],
        157: [False, "2 Samuel 9, 2 Samuel 10, Acts 3, Psalm 70:1-5"],
        158: [False, "2 Samuel 11, 2 Samuel 12, Acts 4:1-22, Psalm 71:1-8"],
        159: [False, "2 Samuel 13, Acts 4:23-37, Acts 5:1-11, Psalm 71:9-18"],
        160: [
            False,
            "2 Samuel 14, 2 Samuel 15:1-12, Acts 5:12-42, Proverbs 14:15-24"
        ],
        161: [
            False,
            "2 Samuel 15:13-37, 2 Samuel 16:1-14, Acts 6, Acts 7:1-19, Psalm 71:19-24"
        ],
        162: [
            False,
            "2 Samuel 16:15-23, 2 Samuel 17, 2 Samuel 18:1-18, Acts 7:20-43, Psalm 72:1-20"
        ],
        163: [
            False,
            "2 Samuel 18:19-33, 2 Samuel 19, Acts 7:44-60, Acts 8:1-3, Psalm 73:1-14"
        ],
        164: [
            False, "2 Samuel 20, 2 Samuel 21, Acts 8:4-40, Proverbs 14:25-35"
        ],
        165: [
            False, "2 Samuel 22, 2 Samuel 23:1-7, Acts 9:1-31, Psalm 73:15-28"
        ],
        166: [
            False,
            "2 Samuel 23:8-39, 2 Samuel 24:1-25, Acts 9:32-43, Acts 10:1-23, Psalms 74:1-9"
        ],
        167: [
            False,
            "1 Kings 1, 1 Kings 2:1-12, Acts 10:23-48, Acts 11:1-18, Psalm 74:10-17"
        ],
        168: [
            False,
            "1 Kings 2:13-46, 1 Kings 3:1-15, Acts 11:19-30, Acts 12:1-19, Proverbs 15:1-10"
        ],
        169: [
            False,
            "1 Kings 3:16-28, 1 Kings 4, 1 Kings 5, Acts 12:19-25, Acts 13:1-12, Psalm 74:18-23"
        ],
        170: [
            False, "1 Kings 6, 1 Kings 7:1-22, Acts 13:13-41, Psalm 75:1-10"
        ],
        171: [
            False,
            "1 Kings 7:23-51, 1 Kings 8:1-21, Acts 13:42-52, Acts 14:1-7, Psalm 76:1-12"
        ],
        172: [
            False,
            "1 Kings 8:22-66, 1 Kings 9:1-9, Acts 14:8-28, Proverbs 15:11-20"
        ],
        173: [
            False,
            "1 Kings 9:10-28, 1 Kings 10, 1 Kings 11:1-13, Acts 15:1-21, Psalm 77:1-9"
        ],
        174: [
            False,
            "1 Kings 11:14-43, 1 Kings 12:1-24, Acts 15:22-41, Psalm 77:10-20"
        ],
        175: [
            False,
            "1 Kings 12:25-33, 1 Kings 13, 1 Kings 14:1-20, Acts 16:1-15, Psalm 78:1-8"
        ],
        176: [
            False,
            "1 Kings 14:21-31, 1 Kings 15, 1 Kings 16:1-7, Acts 16:16-40, Proverbs 15:21-30"
        ],
        177: [
            False,
            "1 Kings 16:8-34, 1 Kings 17, 1 Kings 18:1-15, Acts 17:1-21, Psalm 78:9-16"
        ],
        178: [
            False,
            "1 Kings 18:16-46, 1 Kings 19, Acts 17:22-34, Acts 18:1-8, Psalm 78:17-31"
        ],
        179: [
            False,
            "1 Kings 20, 1 Kings 21, Acts 18:9-28, Acts 19:1-13, Psalm 78:32-39"
        ],
        180: [
            False,
            "1 Kings 22:1-53, Acts 19:14-41, Proverbs 15:31-33, Proverbs 16:1-7"
        ],
        181: [
            False, "2 Kings 1, 2 Kings 2:1-25, Acts 20:1-38, Psalm 78:40-55"
        ],
        182: [
            False, "2 Kings 3, 2 Kings 4:1-37, Acts 21:1-26, Psalm 78:56-72"
        ],
        183: [
            False,
            "2 Kings 4:38-44, 2 Kings 5, 2 Kings 6:1-23, Acts 21:27-40, Acts 22:1-22, Psalm 79:1-13"
        ],
        184: [
            False,
            "2 Kings 6:24-33, 2 Kings 7, 2 Kings 8:1-15, Acts 22:22-30, Acts 23:1-11, Proverbs 16:8-17"
        ],
        185: [
            False, "2 Kings 8:16-29, 2 Kings 9, Acts 23:12-35, Psalm 80:1-7"
        ],
        186: [False, "2 Kings 10, 2 Kings 11, Acts 24:1-27, Psalm 80:8-19"],
        187: [
            False,
            "2 Kings 12, 2 Kings 13, 2 Kings 14:1-22, Acts 25:1-22, Psalm 81:1-7"
        ],
        188: [
            False,
            "2 Kings 14:23-29, 2 Kings 15, Acts 25:23-27, Acts 26:1-23, Proverbs 16:18-27"
        ],
        189: [
            False,
            "2 Kings 16, 2 Kings 17, Acts 26:24-32, Acts 27:1-12, Psalm 81:8-16"
        ],
        190: [
            False, "2 Kings 18, 2 Kings 19:1-13, Acts 27:13-44, Psalm 82:1-8"
        ],
        191: [
            False, "2 Kings 19:14-37, 2 Kings 20, Acts 28:1-16, Psalm 83:1-18"
        ],
        192: [
            False,
            "2 Kings 21, 2 Kings 22, Acts 28:17-31, Proverbs 16:28-33, Proverbs 17:1-4"
        ],
        193: [
            False, "2 Kings 23, 2 Kings 24:1-7, Romans 1:1-17, Psalm 84:1-7"
        ],
        194: [
            False, "2 Kings 24:8-20, 2 Kings 25, Romans 1:18-32, Psalm 84:8-12"
        ],
        195: [
            False,
            "Jonah 1, Jonah 2, Jonah 3, Jonah 4, Romans 2:1-16, Psalm 85:1-7"
        ],
        196: [
            False,
            "Amos 1, Amos 2, Romans 2:17-29, Romans 3:1-8, Proverbs 17:5-14"
        ],
        197: [False, "Amos 3, Amos 4, Romans 3:9-31, Psalm 85:8-13"],
        198: [False, "Amos 5, Romans 4:1-15, Psalm 86:1-10"],
        199: [
            False,
            "Amos 6, Amos 7, Romans 4:16-25, Romans 5:1-11, Psalm 86:11-17"
        ],
        200: [False, "Amos 8, Amos 9, Romans 5:12-21, Proverbs 17:15-24"],
        201: [False, "Hosea 1, Hosea 2, Romans 6:1-14, Psalm 87:1-7"],
        202: [
            False,
            "Hosea 3, Hosea 4, Hosea 5, Romans 6:15-23, Romans 7:1-6, Psalm 88:1-9"
        ],
        203: [False, "Hosea 6, Hosea 7, Romans 7:7-25, Psalm 88:9-18"],
        204: [
            False,
            "Hosea 8, Hosea 9, Romans 8:1-17, Proverbs 17:25-28, Proverbs 18:1-6"
        ],
        205: [False, "Hosea 10, Hosea 11, Romans 8:18-39, Psalm 89:1-8"],
        206: [
            False,
            "Hosea 11, Hosea 12, Hosea 13, Hosea 14, Romans 9:1-21, Psalm 89:9-13"
        ],
        207: [
            False,
            "1 Chronicles 1, 1 Chronicles 2:1-17, Romans 9:22-33, Romans 10:1-4, Psalm 89:14-18"
        ],
        208: [
            False,
            "1 Chronicles 2:18-55, 1 Chronicles 3, 1 Chronicles 4:1-8, Romans 10:5-21, Romans 11:1-10, Proverbs 18:7-16"
        ],
        209: [
            False,
            "1 Chronicles 4:9-43, 1 Chronicles 5, Romans 11:11-32, Psalm 89:19-29"
        ],
        210: [
            False,
            "1 Chronicles 6, Romans 11:33-36, Romans 12:1-21, Psalm 89:30-37"
        ],
        211: [
            False,
            "1 Chronicles 7, 1 Chronicles 8, Romans 13:1-14, Psalm 89:38-45"
        ],
        212: [
            False,
            "1 Chronicles 9, 1 Chronicles 10:1-14, Romans 14:1-18, Proverbs 18:17-24, Proverbs 19:1-2"
        ],
        213: [
            False,
            "1 Chronicles 11, 1 Chronicles 12:1-22, Romans 14:19-23, Romans 15:1-13, Psalm 89:46-52"
        ],
        214: [
            False,
            "1 Chronicles 12:23-40, 1 Chronicles 13, 1 Chronicles 14, Romans 15:14-33, Psalm 90:1-10"
        ],
        215: [
            False,
            "1 Chronicles 15, 1 Chronicles 16:1-36, Romans 16, Psalm 90:11-17"
        ],
        216: [
            False,
            "1 Chronicles 16:37-43, 1 Chronicles 17, 1 Chronicles 18, 1 Corinthians 1:1-17, Proverbs 19:3-12"
        ],
        217: [
            False,
            "1 Chronicles 19, 1 Chronicles 20, 1 Chronicles 21, 1 Corinthians 1:18-31, 1 Corinthians 2:1-5, Psalm 91:1-8"
        ],
        218: [
            False,
            "1 Chronicles 22, 1 Chronicles 23, 1 Corinthians 2:6-16, Psalm 91:9-16"
        ],
        219: [
            False,
            "1 Chronicles 24, 1 Chronicles 25, 1 Chronicles 26:1-19, 1 Corinthians 3, Psalm 92:1-15"
        ],
        220: [
            False,
            "1 Chronicles 26:20-32, 1 Chronicles 27, 1 Corinthians 4, Proverbs 19:13-22"
        ],
        221: [
            False,
            "1 Chronicles 28, 1 Chronicles 29, 1 Corinthians 5, Psalm 93:1-5"
        ],
        222: [False, "2 Chronicles 1:1-17, 1 Corinthians 6, Psalm 94:1-11"],
        223: [
            False,
            "Ecclesiastes 1, Ecclesiastes 2, Ecclesiastes 3:1-22, 1 Corinthians 7:1-16, Psalm 94:12-23"
        ],
        224: [
            False,
            "Ecclesiastes 4, Ecclesiastes 5, Ecclesiastes 6, 1 Corinthians 7:17-35, Proverbs 19:23-29, Proverbs 20:1-4"
        ],
        225: [
            False,
            "Ecclesiastes 7, Ecclesiastes 8, Ecclesiastes 9:1-12, 1 Corinthians 7:36-40, 1 Corinthians 8:1-13, Psalm 95:1-11"
        ],
        226: [
            False,
            "Ecclesiastes 9:13-18, Ecclesiastes 10, Ecclesiastes 11, Ecclesiastes 12, 1 Corinthians 9:1-18, Psalm 96:1-13"
        ],
        227: [
            False,
            "2 Chronicles 2, 2 Chronicles 3, 2 Chronicles 4, 2 Chronicles 5:1, 1 Corinthians 9:19-27, 1 Corinthians 10:1-13, Psalm 97:1-12"
        ],
        228: [
            False,
            "2 Chronicles 5:2-14, 2 Chronicles 6, 2 Chronicles 7:1-10, 1 Corinthians 10:14-33, 1 Corinthians 11:1, Proverbs 20:5-14"
        ],
        229: [
            False,
            "2 Chronicles 7:11-22, 2 Chronicles 8, 2 Chronicles 9, 1 Corinthians 11:2-34, Psalm 98:1-9"
        ],
        230: [
            False,
            "Song 1, Song 2, Song 3, Song 4, 1 Corinthians 12:1-26, Psalm 99:1-9"
        ],
        231: [
            False,
            "Song 5, Song 6, Song 7, Song 8, 1 Corinthians 12:27-31, 1 Corinthians 13:1-13, Psalm 100:1-5"
        ],
        232: [
            False,
            "2 Chronicles 10, 2 Chronicles 11, 2 Chronicles 12, 1 Corinthians 14:1-19, Proverbs 20:15-24"
        ],
        233: [
            False,
            "2 Chronicles 13, 2 Chronicles 14, 2 Chronicles 15, 1 Corinthians 14:20-40, Psalm 101:1-8"
        ],
        234: [
            False,
            "2 Chronicles 16, 2 Chronicles 17, 2 Chronicles 18:1-27, 1 Corinthians 15:1-34, Psalm 102:1-11"
        ],
        235: [
            False,
            "2 Chronicles 18:28-34, 2 Chronicles 19, 2 Chronicles 20, 1 Corinthians 15:35-49, Psalm 102:12-17"
        ],
        236: [
            False,
            "2 Chronicles 21, 2 Chronicles 22, 2 Chronicles 23, 1 Corinthians 15:50-58, 1 Corinthians 16:1-4, Proverbs 20:25-30, Proverbs 21:1-4"
        ],
        237: [
            False,
            "2 Chronicles 24, 2 Chronicles 25, 1 Corinthians 16:5-24, Psalm 102:18-28"
        ],
        238: [
            False,
            "2 Chronicles 26, 2 Chronicles 27, 2 Chronicles 28, 2 Corinthians 1:1-11, Psalm 103:1-12"
        ],
        239: [
            False,
            "2 Chronicles 29, 2 Chronicles 30, 2 Chronicles 31:1, 2 Corinthians 1:12-22, Psalm 103:13-22"
        ],
        240: [
            False,
            "2 Chronicles 31:2-21, 2 Chronicles 32, 2 Chronicles 33:1-20, 2 Corinthians 1:23, 2 Corinthians 2:1-11, Proverbs 21:5-16"
        ],
        241: [
            False,
            "2 Chronicles 33:21-24, 2 Chronicles 34, 2 Chronicles 35:1-19, 2 Corinthians 2:12-17, 2 Corinthians 3:1-6, Psalm 104:1-18"
        ],
        242: [
            False,
            "2 Chronicles 35:20-27, 2 Chronicles 36, 2 Corinthians 3:7-18, Psalm 104:19-30"
        ],
        243: [
            False,
            "Micah 1, Micah 2, Micah 3, Micah 4, 2 Corinthians 4, Psalm 104:31-35"
        ],
        244: [
            False,
            "Micah 5, Micah 6, Micah 7, 2 Corinthians 5:1-10, Proverbs 21:17-26"
        ],
        245: [
            False,
            "Isaiah 1, Isaiah 2, 2 Corinthians 5:11-21, 2 Corinthians 6:1-2, Psalm 105:1-11"
        ],
        246: [
            False,
            "Isaiah 3, Isaiah 4, Isaiah 5:1-7, 2 Corinthians 6:3-18, 2 Corinthians 7:1, Psalm 105:12-22"
        ],
        247: [
            False,
            "Isaiah 5:8-30, Isaiah 6, Isaiah 7, Isaiah 8:1-10, 2 Corinthians 7:2-16, Psalm 105:23-36"
        ],
        248: [
            False,
            "Isaiah 8:11-22, Isaiah 9, Isaiah 10:1-19, 2 Corinthians 8:1-15, Proverbs 21:27-31, Proverbs 22:1-6"
        ],
        249: [
            False,
            "Isaiah 10:20-34, Isaiah 11, Isaiah 12, Isaiah 13, 2 Corinthians 8:16-24, 2 Corinthians 9:1-5, Psalm 105:37-45"
        ],
        250: [
            False,
            "Isaiah 14, Isaiah 15, Isaiah 16, 2 Corinthians 9:6-15, Psalm 106:1-15"
        ],
        251: [
            False,
            "Isaiah 17, Isaiah 18, Isaiah 19, 2 Corinthians 10, Psalm 106:16-31"
        ],
        252: [
            False,
            "Isaiah 20, Isaiah 21, Isaiah 22, Isaiah 23, 2 Corinthians 11:1-15, Proverbs 22:7-16"
        ],
        253: [
            False,
            "Isaiah 24, Isaiah 25, Isaiah 26, 2 Corinthians 11:16-33, Psalm 106:32-39"
        ],
        254: [
            False,
            "Isaiah 27, Isaiah 28, 2 Corinthians 12:1-10, Psalm 106:40-48"
        ],
        255: [
            False,
            "Isaiah 29, Isaiah 30:1-18, 2 Corinthians 12:11-21, Psalm 107:1-9"
        ],
        256: [
            False,
            "Isaiah 30:19-33, Isaiah 31, Isaiah 32, 2 Corinthians 13, Proverbs 22:17-27"
        ],
        257: [
            False,
            "Isaiah 33, Isaiah 34, Isaiah 35, Galatians 1, Psalm 107:10-22"
        ],
        258: [
            False, "Isaiah 36, Isaiah 37, Galatians 2:1-10, Psalm 107:23-32"
        ],
        259: [
            False,
            "Isaiah 38, Isaiah 39, Isaiah 40, Galatians 2:11-21, Galatians 3:1-9, Psalm 107:33-43"
        ],
        260: [
            False,
            "Isaiah 41, Isaiah 42, Galatians 3:10-25, Proverbs 22:28-29, Proverbs 23:1-9"
        ],
        261: [
            False,
            "Isaiah 43, Isaiah 44:1-23, Galatians 3:26-29, Galatians 4:1-20, Psalm 108:1-5"
        ],
        262: [
            False,
            "Isaiah 44:24-28, Isaiah 45, Isaiah 46, Galatians 4:21-31, Galatians 5:1-6, Psalm 108:6-13"
        ],
        263: [
            False,
            "Isaiah 47, Isaiah 48, Isaiah 49:1-7, Galatians 5:7-26, Psalm 109:1-20"
        ],
        264: [
            False,
            "Isaiah 49:8-26, Isaiah 50, Isaiah 51:1-16, Galatians 6, Proverbs 23:10-18"
        ],
        265: [
            False,
            "Isaiah 51:17-23, Isaiah 52, Isaiah 53, Isaiah 54, Ephesians 1, Psalm 109:21-31"
        ],
        266: [
            False,
            "Isaiah 55, Isaiah 56, Isaiah 57:1-13, Ephesians 2, Psalm 110:1-7"
        ],
        267: [
            False,
            "Isaiah 57:14-21, Isaiah 58, Isaiah 59, Ephesians 3, Psalm 111:1-10"
        ],
        268: [
            False,
            "Isaiah 60, Isaiah 61, Isaiah 62, Ephesians 4:1-16, Proverbs 23:19-28"
        ],
        269: [
            False,
            "Isaiah 63, Isaiah 64, Isaiah 65:1-16, Ephesians 4:17-32, Ephesians 5:1-7, Psalm 112:1-10"
        ],
        270: [
            False,
            "Isaiah 65:17-25, Isaiah 66, Ephesians 5:8-33, Psalm 113:1-9"
        ],
        271: [False, "Nahum 1, Nahum 2, Nahum 3, Ephesians 6, Psalm 114:1-8"],
        272: [
            False,
            "Zephaniah 1, Zephaniah 2, Zephaniah 3, Philippians 1:1-26, Proverbs 23:29-35, Proverbs 24:1-4"
        ],
        273: [
            False,
            "Jeremiah 1, Jeremiah 2:1-30, Philippians 1:27-30, Philippians 2:1-11, Psalm 115:1-11"
        ],
        274: [
            False,
            "Jeremiah 2:31-47, Jeremiah 3, Jeremiah 4:1-9, Philippians 2:12-30, Psalm 115:12-18"
        ],
        275: [
            False,
            "Jeremiah 4:10-31, Jeremiah 5, Philippians 3, Philippians 4:1, Psalm 116:1-11"
        ],
        276: [
            False,
            "Jeremiah 6, Jeremiah 7:1-29, Philippians 4:2-23, Proverbs 24:5-14"
        ],
        277: [
            False,
            "Jeremiah 7:30-34, Jeremiah 8, Jeremiah 9:1-16, Colossians 1:1-23, Psalm 116:12-19"
        ],
        278: [
            False,
            "Jeremiah 9:17-26, Jeremiah 10, Jeremiah 11:1-17, Colossians 1:24-29, Colossians 2:1-5, Psalm 117:1-2"
        ],
        279: [
            False,
            "Jeremiah 11:18-23, Jeremiah 12, Jeremiah 13, Colossians 2:6-23, Psalm 118:1-16"
        ],
        280: [
            False,
            "Jeremiah 14, Jeremiah 15, Colossians 3, Colossians 4:1, Proverbs 24:15-22"
        ],
        281: [
            False,
            "Jeremiah 16, Jeremiah 17, Colossians 4:2-18, Psalm 118:17-29"
        ],
        282: [
            False,
            "Jeremiah 18, Jeremiah 19, Jeremiah 20, 1 Thessalonians 1, 1 Thessalonians 2:1-16, Psalm 119:1-8"
        ],
        283: [
            False,
            "Jeremiah 21, Jeremiah 22, Jeremiah 23:1-8, 1 Thessalonians 2:17-19, 1 Thessalonians 3, Psalm 119:9-16"
        ],
        284: [
            False,
            "Jeremiah 23:9-40, Jeremiah 24, Jeremiah 25:1-14, 1 Thessalonians 4, Proverbs 24:23-34"
        ],
        285: [
            False,
            "Jeremiah 25:15-38, Jeremiah 26, 1 Thessalonians 5, Psalm 119:17-24"
        ],
        286: [
            False,
            "Jeremiah 27, Jeremiah 28, Jeremiah 29:1-23, 2 Thessalonians 1, Psalm 119:25-32"
        ],
        287: [
            False,
            "Jeremiah 29:24-32, Jeremiah 30, Jeremiah 31:1-14, 2 Thessalonians 2, Psalm 119:33-40"
        ],
        288: [
            False,
            "Jeremiah 31:15-40, Jeremiah 32:1-25, 2 Thessalonians 3, Proverbs 25:1-10"
        ],
        289: [
            False,
            "Jeremiah 32:26-44, Jeremiah 33, Jeremiah 34, 1 Timothy 1, Psalm 119:41-48"
        ],
        290: [
            False,
            "Jeremiah 35, Jeremiah 36, Jeremiah 37, 1 Timothy 2, Psalm 119:49-56"
        ],
        291: [
            False,
            "Jeremiah 38, Jeremiah 39, Jeremiah 40:1-6, 1 Timothy 3, Psalm 119:57-64"
        ],
        292: [
            False,
            "Jeremiah 40:7-16, Jeremiah 41, Jeremiah 42, 1 Timothy 4, Proverbs 25:11-20"
        ],
        293: [
            False,
            "Jeremiah 43, Jeremiah 44, Jeremiah 45, 1 Timothy 5, 1 Timothy 6:1-2, Psalm 119:65-72"
        ],
        294: [
            False,
            "Jeremiah 46, Jeremiah 47, 1 Timothy 6:3-21, Psalm 119:73-80"
        ],
        295: [
            False, "Jeremiah 48, Jeremiah 49:1-6, 2 Timothy 1, Psalm 119:81-88"
        ],
        296: [
            False,
            "Jeremiah 49:7-39, Jeremiah 50:1-10, 2 Timothy 2, Proverbs 25:21-28, Proverbs 26:1-2"
        ],
        297: [
            False,
            "Jeremiah 50:11-46, Jeremiah 51:1-14, 2 Timothy 3, Psalm 119:89-96"
        ],
        298: [False, "Jeremiah 51:15-64, 2 Timothy 4, Psalm 119:97-104"],
        299: [False, "Jeremiah 52, Titus 1, Psalm 119:105-112"],
        300: [
            False,
            "Habakkuk 1, Habakkuk 2, Habakkuk 3:1-19, Titus 2, Proverbs 26:3-12"
        ],
        301: [
            False,
            "Lamentations 1, Lamentations 2:1-6, Titus 3, Psalm 119:113-120"
        ],
        302: [
            False,
            "Lamentations 2:7-27, Lamentations 3:1-39, Philemon 1, Psalm 119:121-128"
        ],
        303: [
            False,
            "Lamentations 3:40-66, Lamentations 4, Lamentations 5, Hebrews 1, Psalm 119:129-136"
        ],
        304: [False, "Obadiah 1, Hebrews 2, Proverbs 26:13-22"],
        305: [False, "Joel 1, Joel 2:1-17, Hebrews 3, Psalm 119:137-144"],
        306: [
            False, "Joel 2:18-32, Joel 3, Hebrews 4:1-13, Psalm 119:145-152"
        ],
        307: [
            False,
            "Ezekiel 1, Ezekiel 2, Ezekiel 3, Hebrews 4:14-16, Hebrews 5:1-10, Psalm 119:153-160"
        ],
        308: [
            False,
            "Ezekiel 4, Ezekiel 5, Ezekiel 6, Hebrews 5:11-14, Hebrews 6:1-12, Proverbs 26:23-28, Proverbs 27:1-4"
        ],
        309: [
            False,
            "Ezekiel 7, Ezekiel 8, Ezekiel 9, Hebrews 6:13-20, Hebrews 7:1-10, Psalm 119:161-168"
        ],
        310: [
            False,
            "Ezekiel 10, Ezekiel 11, Ezekiel 12, Hebrews 7:11-28, Psalm 119:169-176"
        ],
        311: [
            False,
            "Ezekiel 13, Ezekiel 14, Ezekiel 15, Hebrews 8, Psalm 120:1-7"
        ],
        312: [False, "Ezekiel 16, Hebrews 9:1-15, Proverbs 27:5-14"],
        313: [False, "Ezekiel 17, Ezekiel 18, Hebrews 9:16-28, Psalm 121:1-8"],
        314: [
            False,
            "Ezekiel 19, Ezekiel 20:1-44, Hebrews 10:1-18, Psalm 122:1-9"
        ],
        315: [
            False,
            "Ezekiel 20:45-49, Ezekiel 21, Ezekiel 22:1-22, Hebrews 10:19-39, Psalm 123:1-4"
        ],
        316: [
            False,
            "Ezekiel 22:23-31, Ezekiel 23, Hebrews 11:1-16, Proverbs 27:15-22"
        ],
        317: [
            False, "Ezekiel 24, Ezekiel 25, Hebrews 11:17-40, Psalm 124:1-8"
        ],
        318: [False, "Ezekiel 26, Ezekiel 27, Hebrews 12:1-13, Psalm 125:1-5"],
        319: [
            False, "Ezekiel 28, Ezekiel 29, Hebrews 12:14-29, Psalm 126:1-6"
        ],
        320: [
            False,
            "Ezekiel 30, Ezekiel 31, Hebrews 13, Proverbs 27:23-27, Proverbs 28:1-6"
        ],
        321: [False, "Ezekiel 32, Ezekiel 33:1-32, James 1, Psalm 127:1-5"],
        322: [
            False,
            "Ezekiel 33:21-33, Ezekiel 34, Ezekiel 35, James 2, Psalm 128:1-6"
        ],
        323: [False, "Ezekiel 36, Ezekiel 37, James 3, Psalm 129:1-8"],
        324: [False, "Ezekiel 38, Ezekiel 39, James 4, Proverbs 28:7-17"],
        325: [False, "Ezekiel 40, James 5, Psalm 130:1-8"],
        326: [
            False,
            "Ezekiel 41, Ezekiel 42, 1 Peter 1, 1 Peter 2:1-3, Psalm 131:1-3"
        ],
        327: [False, "Ezekiel 43, Ezekiel 44, 1 Peter 2:4-25, Psalm 132:1-18"],
        328: [False, "Ezekiel 45, Ezekiel 46, 1 Peter 3, Proverbs 28:18-28"],
        329: [False, "Ezekiel 47, Ezekiel 48, 1 Peter 4, Psalm 133:1-3"],
        330: [False, "Daniel 1, Daniel 2:1-23, 1 Peter 5, Psalm 134:1-3"],
        331: [
            False, "Daniel 2:24-49, Daniel 3:1-12, 2 Peter 1, Psalm 135:1-12"
        ],
        332: [
            False, "Daniel 3:13-30, Daniel 4:1-18, 2 Peter 2, Proverbs 29:1-9"
        ],
        333: [
            False, "Daniel 4:19-37, Daniel 5:1-16, 2 Peter 3, Psalm 135:13-21"
        ],
        334: [
            False,
            "Daniel 5:17-31, Daniel 6:1-28, 1 John 1, 1 John 2, Psalm 136:1-12"
        ],
        335: [
            False, "Daniel 7, Daniel 8:1-14, 1 John 2:12-27, Psalm 136:13-26"
        ],
        336: [
            False,
            "Daniel 8:15-27, Daniel 9:1-19, 1 John 2:28-29, 1 John 3:1-10, Proverbs 29:10-18"
        ],
        337: [
            False,
            "Daniel 9:20-27, Daniel 10, Daniel 11:1, 1 John 3:11-24, 1 John 4:1-6, Psalm 137:1-9"
        ],
        338: [False, "Daniel 11:2-35, 1 John 4:7-21, Psalm 138:1-8"],
        339: [
            False, "Daniel 11:36-45, Daniel 12, 1 John 5:1-21, Psalm 139:1-10"
        ],
        340: [
            False, "Haggai 1, Haggai 2:1-23, 2 John 1:1-13, Proverbs 29:19-27"
        ],
        341: [
            False,
            "Zechariah 1, Zechariah 2, Zechariah 3, Zechariah 4, 3 John 1:1-14, Psalm 139:11-16"
        ],
        342: [
            False,
            "Zechariah 5, Zechariah 6, Zechariah 7, Zechariah 8, Jude 1:1-25, Psalm 139:17-24"
        ],
        343: [
            False,
            "Zechariah 9, Zechariah 10, Zechariah 11, Revelation 1, Psalm 140:1-5"
        ],
        344: [
            False,
            "Zechariah 12, Zechariah 13, Zechariah 14, Revelation 2:1-17, Proverbs 30:1-10"
        ],
        345: [
            False,
            "Esther 1, Esther 2:1-18, Revelation 2:18-29, Revelation 3:1-6, Psalm 140:6-13"
        ],
        346: [
            False,
            "Esther 2:19-23, Esther 3, Esther 4, Esther 5, Revelation 3:7-22, Psalm 141:1-10"
        ],
        347: [
            False, "Esther 6, Esther 7, Esther 8, Revelation 4, Psalm 142:1-11"
        ],
        348: [False, "Esther 9, Esther 10, Revelation 5, Proverbs 30:11-23"],
        349: [
            False, "Malachi 1, Malachi 2:1-16, Revelation 6, Psalm 143:1-12"
        ],
        350: [
            False,
            "Malachi 2:17, Malachi 3, Malachi 4, Revelation 7, Psalm 144:1-8"
        ],
        351: [
            False,
            "Ezra 1, Ezra 2:1-67, Revelation 8, Revelation 9:1-12, Psalm 144:9-15"
        ],
        352: [
            False,
            "Ezra 2:68-70, Ezra 3, Ezra 4:1-5, Revelation 9:13-21, Revelation 10, Proverbs 30:24-33"
        ],
        353: [False, "Ezra 4:6-24, Ezra 5, Revelation 11, Psalm 145:1-7"],
        354: [
            False,
            "Ezra 6, Ezra 7:1-10, Revelation 12, Revelation 13:1, Psalm 145:8-13"
        ],
        355: [
            False,
            "Ezra 7:11-28, Ezra 8:1-14, Revelation 13:1-18, Psalm 145:13-21"
        ],
        356: [
            False,
            "Ezra 8:15-36, Ezra 9:1-15, Revelation 14:1-13, Proverbs 31:1-9"
        ],
        357: [
            False,
            "Ezra 10, Revelation 14:14-20, Revelation 15, Psalm 146:1-10"
        ],
        358: [False, "Nehemiah 1, Nehemiah 2, Revelation 16, Psalm 147:1-11"],
        359: [False, "Nehemiah 3, Nehemiah 4, Revelation 17, Psalm 147:12-20"],
        360: [
            False,
            "Nehemiah 5, Nehemiah 6, Nehemiah 7:1-3, Revelation 18:1-17, Proverbs 31:10-20"
        ],
        361: [
            False,
            "Nehemiah 7:4-73, Nehemiah 8, Revelation 18:17-24, Revelation 19:1-10, Psalm 148:1-6"
        ],
        362: [False, "Nehemiah 9:1-37, Revelation 19:11-21, Psalm 148:7-14"],
        363: [
            False,
            "Nehemiah 9:38, Nehemiah 10, Nehemiah 11:1-21, Revelation 20, Psalm 149:1-9"
        ],
        364: [
            False,
            "Nehemiah 11:22-36, Nehemiah 12:1-47, Revelation 21, Proverbs 31:21-31"
        ],
        365: [False, "Nehemiah 13, Revelation 22, Psalm 150:1-6"],
    }

    translation = (
        "Bible Reading Plan",
        "Today is ",
        "Search: ",
        "Open in Tabs",
        "Hide Checked Items",
        "Show Checked Items",
        "Reset All Items",
        "Save Reading Progress",
        "Day ",
        "",
        "Your reading progress is saved in the following location:",
        "Failed to save your progress locally.  You may need to grant write permission to UBA.",
    )

    def __init__(self, parent):
        super().__init__()
        self.parent = parent
        # set title
        self.setWindowTitle(self.translation[0])
        self.setMinimumSize(830, 500)
        # set variables
        self.setupVariables()
        # setup interface
        self.setupUI()

    def setupVariables(self):
        import copy, os
        from datetime import date
        self.today = date.today()
        self.todayNo = int(format(self.today, '%j'))
        if self.todayNo > 365:
            self.todayNo = 365
        self.progressFile = os.path.join(os.getcwd(), "plugins", "menu",
                                         "{0}.txt".format(self.translation[0]))
        if os.path.isfile(self.progressFile):
            from ast import literal_eval
            with open(self.progressFile, "r") as fileObj:
                self.plan = literal_eval(fileObj.read())
        else:
            self.plan = copy.deepcopy(self.template)
        self.hideCheckedItems = False

    def setupUI(self):
        from qtpy.QtGui import QStandardItemModel
        from qtpy.QtWidgets import (QPushButton, QLabel, QListView,
                                    QAbstractItemView, QHBoxLayout,
                                    QVBoxLayout, QLineEdit)

        mainLayout = QVBoxLayout()

        readingListLayout = QVBoxLayout()

        readingListLayout.addWidget(QLabel(self.translation[0]))
        readingListLayout.addWidget(
            QLabel("{0}{1}".format(self.translation[1], self.today)))

        filterLayout = QHBoxLayout()
        filterLayout.addWidget(QLabel(self.translation[2]))
        self.filterEntry = QLineEdit()
        self.filterEntry.textChanged.connect(self.resetItems)
        filterLayout.addWidget(self.filterEntry)
        readingListLayout.addLayout(filterLayout)

        self.readingList = QListView()
        self.readingList.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.readingListModel = QStandardItemModel(self.readingList)
        self.readingList.setModel(self.readingListModel)
        self.resetItems()
        self.readingListModel.itemChanged.connect(self.itemChanged)
        #print(self.readingList.currentIndex().row())
        #self.readingList.selectionModel().selectionChanged.connect(self.function)
        readingListLayout.addWidget(self.readingList)

        buttonsLayout = QHBoxLayout()

        button = QPushButton(self.translation[3])
        button.clicked.connect(self.openInTabs)
        buttonsLayout.addWidget(button)

        self.hideShowButton = QPushButton(self.translation[4])
        self.hideShowButton.clicked.connect(self.hideShowCheckedItems)
        buttonsLayout.addWidget(self.hideShowButton)

        button = QPushButton(self.translation[6])
        button.clicked.connect(self.resetAllItems)
        buttonsLayout.addWidget(button)

        button = QPushButton(self.translation[7])
        button.clicked.connect(self.saveProgress)
        buttonsLayout.addWidget(button)

        mainLayout.addLayout(readingListLayout)
        mainLayout.addLayout(buttonsLayout)

        self.setLayout(mainLayout)

    def itemChanged(self, standardItem):
        from qtpy.QtCore import Qt
        key = int(standardItem.text().split(".")[0])
        if standardItem.checkState() is Qt.CheckState.Checked:
            self.plan[key][0] = True
        elif standardItem.checkState() is Qt.CheckState.Unchecked:
            self.plan[key][0] = False
        if self.hideCheckedItems:
            self.resetItems()

    def resetItems(self):
        from qtpy.QtGui import QStandardItem
        from qtpy.QtCore import Qt
        # Empty the model before reset
        self.readingListModel.clear()
        # Reset
        index = 0
        todayIndex = None
        filterEntry = self.filterEntry.text()
        for key, value in self.plan.items():
            checked, passages = value
            if not (self.hideCheckedItems and checked) and (
                    filterEntry == "" or
                (filterEntry != ""
                 and filterEntry.lower() in passages.lower())):
                item = QStandardItem("{0}. {1}".format(key, passages))
                item.setToolTip("{0}{1}{2}".format(self.translation[8], key,
                                                   self.translation[9]))
                if key == self.todayNo:
                    todayIndex = index
                item.setCheckable(True)
                item.setCheckState(Qt.CheckState.Checked if checked else Qt.
                                   CheckState.Unchecked)
                self.readingListModel.appendRow(item)
                index += 1
        if todayIndex is not None:
            self.readingList.setCurrentIndex(
                self.readingListModel.index(todayIndex, 0))

    def hideShowCheckedItems(self):
        self.hideCheckedItems = not self.hideCheckedItems
        self.resetItems()
        self.hideShowButton.setText(self.translation[5] if self.
                                    hideCheckedItems else self.translation[4])

    def resetAllItems(self):
        import copy
        self.plan = copy.deepcopy(self.template)
        self.resetItems()

    def translateIntoChinese(self):
        import copy, pprint
        from BibleBooks import BibleBooks
        plan = copy.deepcopy(self.template)
        filePath = "{0}_zh".format(self.progressFile)
        with open(filePath, "w", encoding="utf-8") as fileObj:
            fileObj.write(pprint.pformat(plan))
        with open(filePath, "r") as fileObj:
            text = fileObj.read()
        translateDict = {}
        bookNames = []
        for key, value in BibleBooks.eng.items():
            bookName = value[-1]
            bookNames.append(bookName)
            translateDict[bookName] = BibleBooks.sc[key][-1]
        bookNames = sorted(bookNames, key=len, reverse=True)
        #print(bookNames)
        for name in bookNames:
            text = text.replace(name, translateDict[name])
        text = text.replace("Psalm", "诗篇")
        with open(filePath, "w", encoding="utf-8") as fileObj:
            fileObj.write(text)

    def saveProgress(self):
        import pprint
        from qtpy.QtWidgets import QMessageBox
        try:
            with open(self.progressFile, "w", encoding="utf-8") as fileObj:
                fileObj.write(pprint.pformat(self.plan))
            message = "{0}\n'{1}'".format(self.translation[10],
                                          self.progressFile)
        except:
            message = self.translation[11]
        QMessageBox.information(self, self.translation[0], message)

    def openInTabs(self):
        dayNo = self.readingList.currentIndex().row() + 1
        todayReading = self.plan[dayNo][-1].split(", ")
        openBibleWindowContentOnNextTab = config.openBibleWindowContentOnNextTab
        config.openBibleWindowContentOnNextTab = True
        for reading in todayReading:
            command = "MAIN:::{0}".format(reading)
            self.parent.runTextCommand(command)
        config.openBibleWindowContentOnNextTab = openBibleWindowContentOnNextTab
        self.close()
示例#5
0
class BibleReadingPlan(QWidget):

    template = {
        # Source / credits of the following plan: https://www.biblica.com/resources/reading-plans/
        1: [False, '創世記 1, 創世記 2:1-17, 馬太福音 1:1-25, 詩篇 1:1-6'],
        2:
        [False, '創世記 2:18-25, 創世記 3, 創世記 4:1-16, 馬太福音 2:1-18, 詩篇 '
         '2:1-12'],
        3: [
            False, '創世記 4:17-26, 創世記 5, 創世記 6, 馬太福音 2:19-23, 馬太福音 3, 詩篇 '
            '3:1-8'
        ],
        4: [False, '創世記 7, 創世記 8, 創世記 9:1-17, 馬太福音 4:1-22, 箴言 1:1-7'],
        5: [
            False, '創世記 9:18-29, 創世記 10, 創世記 11:1-9, 馬太福音 4:23-25, 馬太福音 '
            '5:1-20, 詩篇 4:1-8'
        ],
        6: [False, '創世記 11:10-32, 創世記 12, 創世記 13, 馬太福音 5:21-42, 詩篇 5:1-12'],
        7:
        [False, '創世記 14, 創世記 15, 創世記 16, 馬太福音 5:43-48, 馬太福音 6:1-24, '
         '詩篇 6'],
        8: [False, '創世記 17, 創世記 18, 馬太福音 6:25-34, 馬太福音 7:1-23, 箴言 '
            '1:8-19'],
        9:
        [False, '創世記 19, 創世記 20:1-18, 馬太福音 7:24-29, 馬太福音 8:1-22, 詩篇 '
         '7:1-9'],
        10: [
            False, '創世記 21, 創世記 22, 創世記 23, 馬太福音 8:23-34, 馬太福音 9:1-13, '
            '詩篇 7:10-17'
        ],
        11: [False, '創世記 24:1-67, 馬太福音 9:14-38, 詩篇 8:1-9'],
        12: [False, '創世記 25, 創世記 26, 馬太福音 10:1-31, 箴言 1:20-33'],
        13: [
            False, '創世記 27, 創世記 28:1-22, 馬太福音 10:32-42, 馬太福音 11:1-15, 詩篇 '
            '9:1-6'
        ],
        14: [False, '創世記 29, 創世記 30, 馬太福音 11:16-30, 詩篇 9:7-12'],
        15: [False, '創世記 31:1-55, 馬太福音 12:1-21, 詩篇 9:13-20'],
        16: [False, '創世記 32, 創世記 33, 馬太福音 12:22-45, 箴言 2:1-11'],
        17:
        [False, '創世記 34, 創世記 35, 馬太福音 12:46-50, 馬太福音 13:1-17, 詩篇 '
         '10:1-11'],
        18: [False, '創世記 36, 創世記 37, 馬太福音 13:18-35, 詩篇 10:12-18'],
        19: [False, '創世記 38, 創世記 39, 馬太福音 13:36-58, 詩篇 11:1-7'],
        20: [False, '創世記 40, 創世記 41:1-40, 馬太福音 14:1-21, 箴言 2:12-22'],
        21: [
            False, '創世記 41:41-57, 創世記 42, 馬太福音 14:22-36, 馬太福音 15:1-9, 詩篇 '
            '12:1-8'
        ],
        22: [False, '創世記 43, 創世記 44, 馬太福音 15:10-39, 詩篇 13:1-6'],
        23: [False, '創世記 45, 創世記 46, 創世記 47:1-12, 馬太福音 16:1-20, 詩篇 14:1-7'],
        24: [
            False, '創世記 47:13-31, 創世記 48, 馬太福音 16:21-28, 馬太福音 17:1-13, '
            '箴言 3:1-10'
        ],
        25: [False, '創世記 49, 創世記 50, 馬太福音 17:14-27, 馬太福音 18:1-9, 詩篇 15:1-5'],
        26: [False, '約伯記 1, 約伯記 2, 約伯記 3, 馬太福音 18:10-35, 詩篇 16:1-11'],
        27: [False, '約伯記 4, 約伯記 5, 約伯記 6, 約伯記 7, 馬太福音 19:1-15, 詩篇 17:1-5'],
        28: [False, '約伯記 8, 約伯記 9, 約伯記 10, 馬太福音 19:16-30, 箴言 3:11-20'],
        29:
        [False, '約伯記 11, 約伯記 12, 約伯記 13, 約伯記 14, 馬太福音 20:1-19, 詩篇 17:6-12'],
        30:
        [False, '約伯記 15, 約伯記 16, 約伯記 17, 約伯記 18, 馬太福音 20:20-34, 詩篇 17:13-15'],
        31: [False, '約伯記 19, 約伯記 20, 約伯記 21, 馬太福音 21:1-17, 詩篇 18:1-6'],
        32: [False, '約伯記 22, 約伯記 23, 約伯記 24, 馬太福音 21:18-32, 箴言 3:21-35'],
        33: [
            False,
            '約伯記 25, 約伯記 26, 約伯記 27, 約伯記 28, 約伯記 29, 馬太福音 21:33-46, 馬太福音 '
            '22:1-14, 詩篇 18:7-15'
        ],
        34: [False, '約伯記 30, 約伯記 31, 約伯記 32, 馬太福音 22:15-46, 詩篇 18:16-24'],
        35: [False, '約伯記 33, 約伯記 34, 馬太福音 23:1-39, 詩篇 18:25-36'],
        36: [False, '約伯記 35, 約伯記 36, 約伯記 37, 馬太福音 24:1-31, 箴言 4:1-9'],
        37: [
            False,
            '約伯記 38, 約伯記 39, 約伯記 40:1-2, 馬太福音 24:32-51, 馬太福音 25:1-13, 詩篇 '
            '18:37-42'
        ],
        38: [False, '約伯記 40:3-24, 約伯記 41, 約伯記 42, 馬太福音 25:14-46, 詩篇 18:43-50'],
        39: [False, '出埃及記 1, 出埃及記 2, 出埃及記 3, 馬太福音 26:1-30, 詩篇 19:1-6'],
        40: [False, '出埃及記 4, 出埃及記 5, 出埃及記 6:1-12, 馬太福音 26:31-46, 箴言 4:10-19'],
        41: [False, '出埃及記 6:13-30, 出埃及記 7,出埃及記 8, 馬太福音 26:47-68, 詩篇 19:7-14'],
        42: [False, '出埃及記 9, 出埃及記 10, 馬太福音 26:69-75, 馬太福音 27:1-10, 詩篇 20:1-9'],
        43: [False, '出埃及記 11, 出埃及記 12, 馬太福音 27:11-44, 詩篇 21:1-7'],
        44: [False, '出埃及記 13, 出埃及記 14, 馬太福音 27:45-66, 箴言 4:20-27'],
        45: [False, '出埃及記 15, 出埃及記 16, 馬太福音 28:1-20, 詩篇 21:8-13'],
        46: [False, '出埃及記 17, 出埃及記 18, 馬可福音 1:1-28, 詩篇 22:1-11'],
        47:
        [False, '出埃及記 19, 出埃及記 20, 馬可福音 1:29-45, 馬可福音 2:1-17, 詩篇 22:12-21'],
        48: [False, '出埃及記 21, 出埃及記 22, 馬可福音 2:18-27, 馬可福音 3:1-30, 箴言 5:1-14'],
        49:
        [False, '出埃及記 23, 出埃及記 24, 馬可福音 3:31-35, 馬可福音 4:1-29, 詩篇 22:22-31'],
        50: [False, '出埃及記 25, 出埃及記 26, 馬可福音 4:30-41, 馬可福音 5:1-20, 詩篇 23:1-6'],
        51: [False, '出埃及記 27, 出埃及記 28, 馬可福音 5:21-43, 馬可福音 6:1-6, 詩篇 24:1-10'],
        52: [False, '出埃及記 29, 出埃及記 30, 馬可福音 6:7-29, 箴言 5:15-23'],
        53: [False, '出埃及記 31, 出埃及記 32, 出埃及記 33:1-6, 馬可福音 6:30-56, 詩篇 25:1-7'],
        54: [False, '出埃及記 33:7-23, 出埃及記 34, 馬可福音 7:1-30, 詩篇 25:8-15'],
        55:
        [False, '出埃及記 35, 出埃及記 36, 馬可福音 7:31-37, 馬可福音 8:1-13, 詩篇 25:16-22'],
        56: [False, '出埃及記 37, 出埃及記 38, 馬可福音 8:14-38, 馬可福音 9:1, 箴言 6:1-11'],
        57: [False, '出埃及記 39, 出埃及記 40, 馬可福音 9:2-32, 詩篇 26:1-12'],
        58: [
            False, '利未記 1, 利未記 2, 利未記 3, 馬可福音 9:33-50, 馬可福音 10:1-12, '
            '詩篇 27:1-6'
        ],
        59: [False, '利未記 4, 利未記 5:1-13, 馬可福音 10:13-31, 詩篇 27:7-14'],
        60: [
            False, '利未記 5:14-19, 利未記 6, 利未記 7:1-10, 馬可福音 10:32-52, '
            '箴言 6:12-19'
        ],
        61: [False, '利未記 7:11-38, 利未記 8, 馬可福音 11:1-27, 詩篇 28'],
        62: [False, '利未記 9, 利未記 10, 馬可福音 11:28-33, 馬可福音 12:1-12, 詩篇 29'],
        63: [False, '利未記 11, 利未記 12, 馬可福音 12:13-27, 詩篇 30:1-7'],
        64: [False, '利未記 13:1-59, 馬可福音 12:28-44, 箴言 6:20-29'],
        65: [False, '利未記 14:1-57, 馬可福音 13:1-31, 詩篇 30:8-12'],
        66: [False, '利未記 15, 利未記 16, 馬可福音 13:32-37, 馬可福音 14:1-16, 詩篇 31:1-8'],
        67: [False, '利未記 17, 利未記 18, 馬可福音 14:17-42, 詩篇 31:9-18'],
        68: [False, '利未記 19, 利未記 20, 馬可福音 14:43-72, 箴言 6:30-35'],
        69: [False, '利未記 21, 利未記 22, 馬可福音 15:1-32, 詩篇 31:19-24'],
        70: [False, '利未記 23, 利未記 24, 馬可福音 15:33-47, 詩篇 32'],
        71: [False, '利未記 25, 利未記 26:1-13, 馬可福音 16:1-20, 詩篇 33:1-11'],
        72: [False, '利未記 26:14-46, 利未記 27, 路加福音 1:1-25, 箴言 7:1-5'],
        73: [False, '民數記 1, 民數記 2:1-9, 路加福音 1:26-38, 詩篇 33:12-22'],
        74: [False, '民數記 2:10-34, 民數記 3, 路加福音 1:39-56, 詩篇 34:1-10'],
        75: [False, '民數記 4, 民數記 5:1-10, 路加福音 1:57-80, 詩篇 34:11-22'],
        76: [False, '民數記 5:11-31, 民數記 6:1-27, 路加福音 2:1-20, 箴言 7:6-20'],
        77: [False, '民數記 7:1-65, 路加福音 2:21-40, 詩篇 35:1-10'],
        78: [
            False, '民數記 7:66-89, 民數記 8, 民數記 9:1-14, 路加福音 2:41-52, 詩篇 '
            '35:11-18'
        ],
        79: [
            False, '民數記 9:15-23, 民數記 10, 民數記 11:1-3, 路加福音 3:1-22, 詩篇 '
            '35:19-28'
        ],
        80: [
            False, '民數記 11:4-35, 民數記 12, 民數記 13:1-25, 路加福音 3:23-38, 路加福音 '
            '4:1-13, 箴言 7:21-27'
        ],
        81: [False, '民數記 13:26-33, 民數記 14, 路加福音 4:14-37, 詩篇 36:1-12'],
        82:
        [False, '民數記 15, 民數記 16:1-35, 路加福音 4:38-44, 路加福音 5:1-16, 詩篇 37:1-9'],
        83: [False, '民數記 16:36-50, 民數記 17, 民數記 18, 路加福音 5:17-32, 詩篇 37:10-20'],
        84: [
            False, '民數記 19, 民數記 20, 民數記 21:1-3, 路加福音 5:33-39, 路加福音 6:1-11, '
            '箴言 8:1-11'
        ],
        85: [False, '民數記 21:4-35, 民數記 22:1-20, 路加福音 6:12-36, 詩篇 37:21-31'],
        86: [
            False, '民數記 22:21-41, 民數記 23:1-26, 路加福音 6:37-49, 路加福音 7:1-10, 詩篇 '
            '37:32-40'
        ],
        87: [False, '民數記 23:27-30, 民數記 24, 民數記 25, 路加福音 7:11-35, 詩篇 38:1-11'],
        88: [False, '民數記 26, 民數記 27:1-11, 路加福音 7:36-50, 箴言 8:12-21'],
        89: [
            False, '民數記 27:12-23, 民數記 28, 民數記 29:1-11, 路加福音 8:1-18, 詩篇 '
            '38:12-22'
        ],
        90: [
            False, '民數記 29:12-40, 民數記 30, 民數記 31:1-24, 路加福音 8:19-39, 詩篇 '
            '39:1-13'
        ],
        91:
        [False, '民數記 31:25-54, 民數記 32, 路加福音 8:40-56, 路加福音 9:1-9, 詩篇 40:1-8'],
        92: [False, '民數記 33, 民數記 34, 路加福音 9:10-27, 箴言 8:22-31'],
        93: [False, '民數記 35, 民數記 36:1-12, 路加福音 9:28-56, 詩篇 40:9-17'],
        94:
        [False, '申命記 1, 申命記 2:1-23, 路加福音 9:57-62, 路加福音 10:1-24, 詩篇 '
         '41:1-6'],
        95: [
            False, '申命記 2:24-37, 申命記 3, 申命記 4:1-14, 路加福音 10:25-42, '
            '路加福音 11:1-4, 詩篇 41:7-13'
        ],
        96: [False, '申命記 4:15-49, 申命記 5, 路加福音 11:5-32, 箴言 8:32-36'],
        97: [False, '申命記 6, 申命記 7, 申命記 8, 路加福音 11:33-54, 詩篇 '
             '42:1-6'],
        98: [False, '申命記 9, 申命記 10, 路加福音 12:1-34, 詩篇 42:7-11'],
        99: [False, '申命記 11, 申命記 12, 路加福音 12:35-59, 詩篇 43:1-5'],
        100: [False, '申命記 13, 申命記 14, 路加福音 13:1-30, 箴言 9:1-12'],
        101: [
            False, '申命記 15, 申命記 16:1-20, 路加福音 13:31-35, 路加福音 14:1-14, '
            '詩篇 44:1-12'
        ],
        102: [
            False, '申命記 16:21-22, 申命記 17, 申命記 18, 路加福音 14:15-35, '
            '詩篇 44:13-26'
        ],
        103: [False, '申命記 19, 申命記 20, 路加福音 15:1-32, 詩篇 45:1-9'],
        104: [False, '申命記 21, 申命記 22, 路加福音 16:1-18, 箴言 9:13-18'],
        105: [
            False, '申命記 23, 申命記 24, 申命記 25:1-19, 路加福音 16:19-31, '
            '路加福音 17:1-10, 詩篇 45:10-17'
        ],
        106: [
            False, '申命記 26, 申命記 27, 申命記 28:1-14, 路加福音 17:11-37, '
            '詩篇 46:1-11'
        ],
        107: [False, '申命記 28:15-68, 路加福音 18:1-30, 詩篇 47:1-9'],
        108: [
            False, '申命記 29, 申命記 30:1-10, 路加福音 18:31-43, 路加福音 19:1-10, '
            '箴言 10:1-10'
        ],
        109: [False, '申命記 30:11-20, 申命記 31:1-29, 路加福音 19:11-44, 詩篇 '
              '48:1-8'],
        110: [
            False, '申命記 31:30, 申命記 32, 路加福音 19:45-48, 路加福音 20:1-26, 詩篇 '
            '48:9-14'
        ],
        111: [
            False, '申命記 33, 申命記 34:1-12, 路加福音 20:27-47, 路加福音 21:1-4, 詩篇 '
            '49:1-20'
        ],
        112: [False, '約書亞記 1, 約書亞記 2, 路加福音 21:5-38, 箴言 10:11-20'],
        113: [False, '約書亞記 3, 約書亞記 4, 約書亞記 5:1-12, 路加福音 22:1-38, 詩篇 50:1-15'],
        114: [
            False, '約書亞記 5:13-15, 約書亞記 6, 約書亞記 7, 路加福音 22:39-62, 詩篇 50:16-23'
        ],
        115:
        [False, '約書亞記 8, 約書亞記 9:1-15, 路加福音 22:63-71, 路加福音 23:1-25, 詩篇 51:1-9'],
        116: [False, '約書亞記 9:16-27, 約書亞記 10, 路加福音 23:26-56, 箴言 10:21-30'],
        117: [False, '約書亞記 11, 約書亞記 12, 路加福音 24:1-35, 詩篇 51:10-19'],
        118: [False, '約書亞記 13, 約書亞記 14, 路加福音 24:36-53, 詩篇 52:1-9'],
        119: [False, '約書亞記 15, 約書亞記 16, 約翰福音 1:1-28, 詩篇 53:1-6'],
        120: [
            False, '約書亞記 17, 約書亞記 18, 約翰福音 1:29-51, 箴言 10:31-32, 箴言 '
            '11:1-8'
        ],
        121: [False, '約書亞記 19, 約書亞記 20, 約書亞記 21:1-19, 約翰福音 2:1-25, 詩篇 54:1-7'],
        122: [False, '約書亞記 21:20-45, 約書亞記 22, 約翰福音 3:1-21, 詩篇 55:1-11'],
        123: [False, '約書亞記 23, 約書亞記 24, 約翰福音 3:22-36, 詩篇 55:12-23'],
        124: [False, '士師記 1, 士師記 2:1-5, 約翰福音 4:1-26, 箴言 11:9-18'],
        125: [False, '士師記 2:6-23, 士師記 3, 約翰福音 4:27-42, 詩篇 56:1-13'],
        126: [False, '士師記 4, 士師記 5, 約翰福音 4:43-54, 約翰福音 5:1-15, 詩篇 57:1-6'],
        127: [False, '士師記 6, 士師記 7:1-8, 約翰福音 5:16-30, 詩篇 57:7-11'],
        128: [False, '士師記 7:8-25, 士師記 8, 約翰福音 5:31-47, 箴言 11:19-28'],
        129: [False, '士師記 9, 約翰福音 6:1-24, 詩篇 58:1-11'],
        130: [False, '士師記 10, 士師記 11, 約翰福音 6:25-59, 詩篇 59:1-8'],
        131: [False, '士師記 12, 士師記 13, 約翰福音 6:60-71, 約翰福音 7:1-13, 詩篇 59:9-19'],
        132: [
            False, '士師記 14, 士師記 15, 約翰福音 7:14-44, 箴言 11:29-31, 箴言 '
            '12:1-7'
        ],
        133: [False, '士師記 16, 士師記 17, 約翰福音 7:45-53, 約翰福音 8:1-11, 詩篇 60:1-4'],
        134: [False, '士師記 18, 士師記 19, 約翰福音 8:12-30, 詩篇 60:5-12'],
        135: [False, '士師記 20, 士師記 21, 約翰福音 8:31-59, 詩篇 61:1-8'],
        136: [False, '路得記 1, 路得記 2, 約翰福音 9:1-34, 箴言 12:8-17'],
        137: [False, '路得記 3, 路得記 4, 約翰福音 9:35-41, 約翰福音 10:1-21, 詩篇 62:1-12'],
        138: [False, '撒母耳記上 1, 撒母耳記上 2:1-26, 約翰福音 10:22-42, 詩篇 63:1-11'],
        139: [
            False, '撒母耳記上 2:27-36, 撒母耳記上 3, 撒母耳記上 4, 約翰福音 11:1-44, 詩篇 64:1-10'
        ],
        140: [
            False, '撒母耳記上 5, 撒母耳記上 6, 撒母耳記上 7, 約翰福音 11:45-57, 約翰福音 12:1-11, '
            '箴言 12:18-27'
        ],
        141: [
            False, '撒母耳記上 8, 撒母耳記上 9, 撒母耳記上 10:1-8, 約翰福音 12:12-26, 詩篇 65:1-13'
        ],
        142: [
            False, '撒母耳記上 10:9-27, 撒母耳記上 11, 撒母耳記上 12, 約翰福音 12:37-50, 約翰福音 '
            '13:1-17, 詩篇 66:1-12'
        ],
        143: [False, '撒母耳記上 13, 撒母耳記上 14:1-23, 約翰福音 13:18-38, 詩篇 66:13-20'],
        144: [
            False, '撒母耳記上 14:24-52, 撒母耳記上 15, 約翰福音 14:1-31, 箴言 12:28, 箴言 '
            '13:1-9'
        ],
        145: [
            False, '撒母耳記上 16, 撒母耳記上 17:1-37, 約翰福音 15, 約翰福音 16:1-4, 詩篇 67:1-7'
        ],
        146: [
            False, '撒母耳記上 17:38-58, 撒母耳記上 18, 約翰福音 16:5-33, 約翰福音 17:1-5, 詩篇 '
            '68:1-6'
        ],
        147: [False, '撒母耳記上 19, 撒母耳記上 20, 約翰福音 17:6-26, 詩篇 68:7-14'],
        148: [
            False, '撒母耳記上 21, 撒母耳記上 22, 撒母耳記上 23, 約翰福音 18:1-24, 箴言 '
            '13:10-19'
        ],
        149: [False, '撒母耳記上 24, 撒母耳記上 25, 約翰福音 18:25-40, 詩篇 68:15-20'],
        150: [
            False, '撒母耳記上 26, 撒母耳記上 27, 撒母耳記上 28, 約翰福音 19:1-27, 詩篇 68:21-27'
        ],
        151: [
            False, '撒母耳記上 29, 撒母耳記上 30, 撒母耳記上 31, 約翰福音 19:28-42, 約翰福音 20:1-9, '
            '詩篇 68:28-35'
        ],
        152: [
            False, '撒母耳記下 1, 撒母耳記下 2:1-7, 約翰福音 20:10-31, 箴言 13:20-25, 箴言 '
            '14:1-4'
        ],
        153: [False, '撒母耳記下 2:8-32, 撒母耳記下 3:1-21, 約翰福音 21:1-25, 詩篇 69:1-12'],
        154: [
            False, '撒母耳記下 3:22-39, 撒母耳記下 4, 撒母耳記下 5:1-5, 使徒行傳 1:1-22, 詩篇 '
            '69:13-28'
        ],
        155: [
            False, '撒母耳記下 5:6-25, 撒母耳記下 6, 使徒行傳 1:23-26, 使徒行傳 2:1-21, 詩篇 '
            '69:29-36'
        ],
        156: [False, '撒母耳記下 7, 撒母耳記下 8, 使徒行傳 2:22-47, 箴言 14:4-14'],
        157: [False, '撒母耳記下 9, 撒母耳記下 10, 使徒行傳 3, 詩篇 70:1-5'],
        158: [False, '撒母耳記下 11, 撒母耳記下 12, 使徒行傳 4:1-22, 詩篇 71:1-8'],
        159: [False, '撒母耳記下 13, 使徒行傳 4:23-37, 使徒行傳 5:1-11, 詩篇 71:9-18'],
        160: [False, '撒母耳記下 14, 撒母耳記下 15:1-12, 使徒行傳 5:12-42, 箴言 14:15-24'],
        161: [
            False, '撒母耳記下 15:13-37, 撒母耳記下 16:1-14, 使徒行傳 6, 使徒行傳 7:1-19, 詩篇 '
            '71:19-24'
        ],
        162: [
            False, '撒母耳記下 16:15-23, 撒母耳記下 17, 撒母耳記下 18:1-18, 使徒行傳 7:20-43, 詩篇 '
            '72:1-20'
        ],
        163: [
            False, '撒母耳記下 18:19-33, 撒母耳記下 19, 使徒行傳 7:44-60, 使徒行傳 8:1-3, 詩篇 '
            '73:1-14'
        ],
        164: [False, '撒母耳記下 20, 撒母耳記下 21, 使徒行傳 8:4-40, 箴言 14:25-35'],
        165: [False, '撒母耳記下 22, 撒母耳記下 23:1-7, 使徒行傳 9:1-31, 詩篇 73:15-28'],
        166: [
            False,
            '撒母耳記下 23:8-39, 撒母耳記下 24:1-25, 使徒行傳 9:32-43, 使徒行傳 10:1-23, 詩篇 '
            '74:1-9'
        ],
        167: [
            False, '列王紀上 1, 列王紀上 2:1-12, 使徒行傳 10:23-48, 使徒行傳 11:1-18, 詩篇 '
            '74:10-17'
        ],
        168: [
            False,
            '列王紀上 2:13-46, 列王紀上 3:1-15, 使徒行傳 11:19-30, 使徒行傳 12:1-19, 箴言 '
            '15:1-10'
        ],
        169: [
            False,
            '列王紀上 3:16-28, 列王紀上 4, 列王紀上 5, 使徒行傳 12:19-25, 使徒行傳 13:1-12, '
            '詩篇 74:18-23'
        ],
        170: [False, '列王紀上 6, 列王紀上 7:1-22, 使徒行傳 13:13-41, 詩篇 75:1-10'],
        171: [
            False, '列王紀上 7:23-51, 列王紀上 8:1-21, 使徒行傳 13:42-52, 使徒行傳 14:1-7, 詩篇 '
            '76:1-12'
        ],
        172: [False, '列王紀上 8:22-66, 列王紀上 9:1-9, 使徒行傳 14:8-28, 箴言 15:11-20'],
        173: [
            False, '列王紀上 9:10-28, 列王紀上 10, 列王紀上 11:1-13, 使徒行傳 15:1-21, 詩篇 '
            '77:1-9'
        ],
        174: [
            False, '列王紀上 11:14-43, 列王紀上 12:1-24, 使徒行傳 15:22-41, 詩篇 77:10-20'
        ],
        175: [
            False, '列王紀上 12:25-33, 列王紀上 13, 列王紀上 14:1-20, 使徒行傳 16:1-15, 詩篇 '
            '78:1-8'
        ],
        176: [
            False, '列王紀上 14:21-31, 列王紀上 15, 列王紀上 16:1-7, 使徒行傳 16:16-40, 箴言 '
            '15:21-30'
        ],
        177: [
            False, '列王紀上 16:8-34, 列王紀上 17, 列王紀上 18:1-15, 使徒行傳 17:1-21, 詩篇 '
            '78:9-16'
        ],
        178: [
            False, '列王紀上 18:16-46, 列王紀上 19, 使徒行傳 17:22-34, 使徒行傳 18:1-8, 詩篇 '
            '78:17-31'
        ],
        179: [
            False, '列王紀上 20, 列王紀上 21, 使徒行傳 18:9-28, 使徒行傳 19:1-13, 詩篇 78:32-39'
        ],
        180: [False, '列王紀上 22:1-53, 使徒行傳 19:14-41, 箴言 15:31-33, 箴言 16:1-7'],
        181: [False, '列王紀下 1, 列王紀下 2:1-25, 使徒行傳 20:1-38, 詩篇 78:40-55'],
        182: [False, '列王紀下 3, 列王紀下 4:1-37, 使徒行傳 21:1-26, 詩篇 78:56-72'],
        183: [
            False, '列王紀下 4:38-44, 列王紀下 5, 列王紀下 6:1-23, 使徒行傳 21:27-40, 使徒行傳 '
            '22:1-22, 詩篇 79:1-13'
        ],
        184: [
            False, '列王紀下 6:24-33, 列王紀下 7, 列王紀下 8:1-15, 使徒行傳 22:22-30, 使徒行傳 '
            '23:1-11, 箴言 16:8-17'
        ],
        185: [False, '列王紀下 8:16-29, 列王紀下 9, 使徒行傳 23:12-35, 詩篇 80:1-7'],
        186: [False, '列王紀下 10, 列王紀下 11, 使徒行傳 24:1-27, 詩篇 80:8-19'],
        187: [
            False, '列王紀下 12, 列王紀下 13, 列王紀下 14:1-22, 使徒行傳 25:1-22, 詩篇 81:1-7'
        ],
        188: [
            False, '列王紀下 14:23-29, 列王紀下 15, 使徒行傳 25:23-27, 使徒行傳 26:1-23, 箴言 '
            '16:18-27'
        ],
        189: [
            False, '列王紀下 16, 列王紀下 17, 使徒行傳 26:24-32, 使徒行傳 27:1-12, 詩篇 81:8-16'
        ],
        190: [False, '列王紀下 18, 列王紀下 19:1-13, 使徒行傳 27:13-44, 詩篇 82:1-8'],
        191: [False, '列王紀下 19:14-37, 列王紀下 20, 使徒行傳 28:1-16, 詩篇 83:1-18'],
        192: [
            False, '列王紀下 21, 列王紀下 22, 使徒行傳 28:17-31, 箴言 16:28-33, 箴言 '
            '17:1-4'
        ],
        193: [False, '列王紀下 23, 列王紀下 24:1-7, 羅馬書 1:1-17, 詩篇 84:1-7'],
        194: [False, '列王紀下 24:8-20, 列王紀下 25, 羅馬書 1:18-32, 詩篇 84:8-12'],
        195: [False, '約拿書 1, 約拿書 2, 約拿書 3, 約拿書 4, 羅馬書 2:1-16, 詩篇 85:1-7'],
        196: [False, '阿摩司書 1, 阿摩司書 2, 羅馬書 2:17-29, 羅馬書 3:1-8, 箴言 17:5-14'],
        197: [False, '阿摩司書 3, 阿摩司書 4, 羅馬書 3:9-31, 詩篇 85:8-13'],
        198: [False, '阿摩司書 5, 羅馬書 4:1-15, 詩篇 86:1-10'],
        199: [False, '阿摩司書 6, 阿摩司書 7, 羅馬書 4:16-25, 羅馬書 5:1-11, 詩篇 86:11-17'],
        200: [False, '阿摩司書 8, 阿摩司書 9, 羅馬書 5:12-21, 箴言 17:15-24'],
        201: [False, '何西阿書 1, 何西阿書 2, 羅馬書 6:1-14, 詩篇 87:1-7'],
        202: [
            False, '何西阿書 3, 何西阿書 4, 何西阿書 5, 羅馬書 6:15-23, 羅馬書 7:1-6, 詩篇 88:1-9'
        ],
        203: [False, '何西阿書 6, 何西阿書 7, 羅馬書 7:7-25, 詩篇 88:9-18'],
        204: [False, '何西阿書 8, 何西阿書 9, 羅馬書 8:1-17, 箴言 17:25-28, 箴言 18:1-6'],
        205: [False, '何西阿書 10, 何西阿書 11, 羅馬書 8:18-39, 詩篇 89:1-8'],
        206: [
            False, '何西阿書 11, 何西阿書 12, 何西阿書 13, 何西阿書 14, 羅馬書 9:1-21, 詩篇 89:9-13'
        ],
        207: [
            False, '歷代志上 1, 歷代志上 2:1-17, 羅馬書 9:22-33, 羅馬書 10:1-4, '
            '詩篇 89:14-18'
        ],
        208: [
            False, '歷代志上 2:18-55, 歷代志上 3, 歷代志上 4:1-8, 羅馬書 '
            '10:5-21, 羅馬書 11:1-10, 箴言 18:7-16'
        ],
        209: [False, '歷代志上 4:9-43, 歷代志上 5, 羅馬書 11:11-32, 詩篇 89:19-29'],
        210: [False, '歷代志上 6, 羅馬書 11:33-36, 羅馬書 12:1-21, 詩篇 89:30-37'],
        211: [False, '歷代志上 7, 歷代志上 8, 羅馬書 13:1-14, 詩篇 89:38-45'],
        212: [
            False, '歷代志上 9, 歷代志上 10:1-14, 羅馬書 14:1-18, 箴言 '
            '18:17-24, 箴言 19:1-2'
        ],
        213: [
            False, '歷代志上 11, 歷代志上 12:1-22, 羅馬書 14:19-23, 羅馬書 '
            '15:1-13, 詩篇 89:46-52'
        ],
        214: [
            False, '歷代志上 12:23-40, 歷代志上 13, 歷代志上 14, 羅馬書 '
            '15:14-33, 詩篇 90:1-10'
        ],
        215: [False, '歷代志上 15, 歷代志上 16:1-36, 羅馬書 16, 詩篇 90:11-17'],
        216: [
            False, '歷代志上 16:37-43, 歷代志上 17, 歷代志上 18, 哥林多前書 '
            '1:1-17, 箴言 19:3-12'
        ],
        217: [
            False, '歷代志上 19, 歷代志上 20, 歷代志上 21, 哥林多前書 '
            '1:18-31, 哥林多前書 2:1-5, 詩篇 91:1-8'
        ],
        218: [False, '歷代志上 22, 歷代志上 23, 哥林多前書 2:6-16, 詩篇 91:9-16'],
        219: [False, '歷代志上 24, 歷代志上 25, 歷代志上 26:1-19, 哥林多前書 '
              '3, 詩篇 92:1-15'],
        220: [False, '歷代志上 26:20-32, 歷代志上 27, 哥林多前書 4, 箴言 '
              '19:13-22'],
        221: [False, '歷代志上 28, 歷代志上 29, 哥林多前書 5, 詩篇 93:1-5'],
        222: [False, '歷代志下 1:1-17, 哥林多前書 6, 詩篇 94:1-11'],
        223: [False, '傳道書 1, 傳道書 2, 傳道書 3:1-22, 哥林多前書 '
              '7:1-16, 詩篇 94:12-23'],
        224: [
            False, '傳道書 4, 傳道書 5, 傳道書 6, 哥林多前書 7:17-35, '
            '箴言 19:23-29, 箴言 20:1-4'
        ],
        225: [
            False, '傳道書 7, 傳道書 8, 傳道書 9:1-12, 哥林多前書 '
            '7:36-40, 哥林多前書 8:1-13, 詩篇 95:1-11'
        ],
        226: [
            False, '傳道書 9:13-18, 傳道書 10, 傳道書 11, 傳道書 '
            '12, 哥林多前書 9:1-18, 詩篇 96:1-13'
        ],
        227: [
            False,
            '歷代志下 2, 歷代志下 3, 歷代志下 4, 歷代志下 5:1, 哥林多前書 9:19-27, 哥林多前書 10:1-13, 詩篇 97:1-12'
        ],
        228: [
            False,
            '歷代志下 5:2-14, 歷代志下 6, 歷代志下 7:1-10, 哥林多前書 10:14-33, 哥林多前書 11:1, 箴言 20:5-14'
        ],
        229: [
            False, '歷代志下 7:11-22, 歷代志下 8, 歷代志下 9, 哥林多前書 '
            '11:2-34, 詩篇 98:1-9'
        ],
        230: [False, '雅歌 1, 雅歌 2, 雅歌 3, 雅歌 4, 哥林多前書 12:1-26, 詩篇 99:1-9'],
        231: [
            False, '雅歌 5, 雅歌 6, 雅歌 7, 雅歌 8, 哥林多前書 12:27-31, 哥林多前書 '
            '13:1-13, 詩篇 100:1-5'
        ],
        232: [
            False, '歷代志下 10, 歷代志下 11, 歷代志下 12, 哥林多前書 '
            '14:1-19, 箴言 20:15-24'
        ],
        233: [
            False, '歷代志下 13, 歷代志下 14, 歷代志下 15, 哥林多前書 '
            '14:20-40, 詩篇 101:1-8'
        ],
        234: [
            False, '歷代志下 16, 歷代志下 17, 歷代志下 18:1-27, 哥林多前書 '
            '15:1-34, 詩篇 102:1-11'
        ],
        235: [
            False, '歷代志下 18:28-34, 歷代志下 19, 歷代志下 20, 哥林多前書 '
            '15:35-49, 詩篇 102:12-17'
        ],
        236: [
            False, '歷代志下 21, 歷代志下 22, 歷代志下 23, 哥林多前書 '
            '15:50-58, 哥林多前書 16:1-4, 箴言 20:25-30, 箴言 21:1-4'
        ],
        237: [False, '歷代志下 24, 歷代志下 25, 哥林多前書 16:5-24, 詩篇 '
              '102:18-28'],
        238: [
            False, '歷代志下 26, 歷代志下 27, 歷代志下 28, 哥林多後書 '
            '1:1-11, 詩篇 103:1-12'
        ],
        239: [
            False, '歷代志下 29, 歷代志下 30, 歷代志下 31:1, 哥林多後書 '
            '1:12-22, 詩篇 103:13-22'
        ],
        240: [
            False,
            '歷代志下 31:2-21, 歷代志下 32, 歷代志下 33:1-20, 哥林多後書 1:23, 哥林多後書 2:1-11, 箴言 21:5-16'
        ],
        241: [
            False,
            '歷代志下 33:21-24, 歷代志下 34, 歷代志下 35:1-19, 哥林多後書 2:12-17, 哥林多後書 3:1-6, 詩篇 104:1-18'
        ],
        242: [False, '歷代志下 35:20-27, 歷代志下 36, 哥林多後書 3:7-18, 詩篇 '
              '104:19-30'],
        243: [False, '彌迦書 1, 彌迦書 2, 彌迦書 3, 彌迦書 4, 哥林多後書 4, 詩篇 104:31-35'],
        244: [False, '彌迦書 5, 彌迦書 6, 彌迦書 7, 哥林多後書 5:1-10, 箴言 21:17-26'],
        245: [
            False, '以賽亞書 1, 以賽亞書 2, 哥林多後書 5:11-21, 哥林多後書 6:1-2, 詩篇 '
            '105:1-11'
        ],
        246: [
            False, '以賽亞書 3, 以賽亞書 4, 以賽亞書 5:1-7, 哥林多後書 6:3-18, 哥林多後書 '
            '7:1, 詩篇 105:12-22'
        ],
        247: [
            False, '以賽亞書 5:8-30, 以賽亞書 6, 以賽亞書 7, 以賽亞書 8:1-10, 哥林多後書 '
            '7:2-16, 詩篇 105:23-36'
        ],
        248: [
            False, '以賽亞書 8:11-22, 以賽亞書 9, 以賽亞書 10:1-19, 哥林多後書 8:1-15, '
            '箴言 21:27-31, 箴言 22:1-6'
        ],
        249: [
            False, '以賽亞書 10:20-34, 以賽亞書 11, 以賽亞書 12, 以賽亞書 13, 哥林多後書 '
            '8:16-24, 哥林多後書 9:1-5, 詩篇 105:37-45'
        ],
        250: [False, '以賽亞書 14, 以賽亞書 15, 以賽亞書 16, 哥林多後書 9:6-15, 詩篇 106:1-15'],
        251: [False, '以賽亞書 17, 以賽亞書 18, 以賽亞書 19, 哥林多後書 10, 詩篇 106:16-31'],
        252: [
            False, '以賽亞書 20, 以賽亞書 21, 以賽亞書 22, 以賽亞書 23, 哥林多後書 11:1-15, '
            '箴言 22:7-16'
        ],
        253: [
            False, '以賽亞書 24, 以賽亞書 25, 以賽亞書 26, 哥林多後書 11:16-33, 詩篇 '
            '106:32-39'
        ],
        254: [False, '以賽亞書 27, 以賽亞書 28, 哥林多後書 12:1-10, 詩篇 106:40-48'],
        255: [False, '以賽亞書 29, 以賽亞書 30:1-18, 哥林多後書 12:11-21, 詩篇 107:1-9'],
        256: [
            False, '以賽亞書 30:19-33, 以賽亞書 31, 以賽亞書 32, 哥林多後書 13, 箴言 '
            '22:17-27'
        ],
        257: [False, '以賽亞書 33, 以賽亞書 34, 以賽亞書 35, 加拉太書 1, 詩篇 107:10-22'],
        258: [False, '以賽亞書 36, 以賽亞書 37, 加拉太書 2:1-10, 詩篇 107:23-32'],
        259: [
            False, '以賽亞書 38, 以賽亞書 39, 以賽亞書 40, 加拉太書 2:11-21, 加拉太書 3:1-9, '
            '詩篇 107:33-43'
        ],
        260: [
            False, '以賽亞書 41, 以賽亞書 42, 加拉太書 3:10-25, 箴言 22:28-29, 箴言 '
            '23:1-9'
        ],
        261: [
            False, '以賽亞書 43, 以賽亞書 44:1-23, 加拉太書 3:26-29, 加拉太書 4:1-20, 詩篇 '
            '108:1-5'
        ],
        262: [
            False, '以賽亞書 44:24-28, 以賽亞書 45, 以賽亞書 46, 加拉太書 4:21-31, 加拉太書 '
            '5:1-6, 詩篇 108:6-13'
        ],
        263: [
            False, '以賽亞書 47, 以賽亞書 48, 以賽亞書 49:1-7, 加拉太書 5:7-26, 詩篇 109:1-20'
        ],
        264: [
            False, '以賽亞書 49:8-26, 以賽亞書 50, 以賽亞書 51:1-16, 加拉太書 6, 箴言 '
            '23:10-18'
        ],
        265: [
            False, '以賽亞書 51:17-23, 以賽亞書 52, 以賽亞書 53, 以賽亞書 54, 以弗所書 1, 詩篇 '
            '109:21-31'
        ],
        266: [False, '以賽亞書 55, 以賽亞書 56, 以賽亞書 57:1-13, 以弗所書 2, 詩篇 110:1-7'],
        267: [False, '以賽亞書 57:14-21, 以賽亞書 58, 以賽亞書 59, 以弗所書 3, 詩篇 111:1-10'],
        268: [False, '以賽亞書 60, 以賽亞書 61, 以賽亞書 62, 以弗所書 4:1-16, 箴言 23:19-28'],
        269: [
            False, '以賽亞書 63, 以賽亞書 64, 以賽亞書 65:1-16, 以弗所書 4:17-32, 以弗所書 '
            '5:1-7, 詩篇 112:1-10'
        ],
        270: [False, '以賽亞書 65:17-25, 以賽亞書 66, 以弗所書 5:8-33, 詩篇 113:1-9'],
        271: [False, '那鴻書 1, 那鴻書 2, 那鴻書 3, 以弗所書 6, 詩篇 114:1-8'],
        272: [
            False, '西番雅書 1, 西番雅書 2, 西番雅書 3, 腓立比書 1:1-26, 箴言 '
            '23:29-35, 箴言 24:1-4'
        ],
        273: [
            False, '耶利米書 1, 耶利米書 2:1-30, 腓立比書 1:27-30, 腓立比書 2:1-11, '
            '詩篇 115:1-11'
        ],
        274: [
            False, '耶利米書 2:31-47, 耶利米書 3, 耶利米書 4:1-9, 腓立比書 2:12-30, '
            '詩篇 115:12-18'
        ],
        275: [False, '耶利米書 4:10-31, 耶利米書 5, 腓立比書 3, 腓立比書 4:1, 詩篇 '
              '116:1-11'],
        276: [False, '耶利米書 6, 耶利米書 7:1-29, 腓立比書 4:2-23, 箴言 24:5-14'],
        277: [
            False, '耶利米書 7:30-34, 耶利米書 8, 耶利米書 9:1-16, 歌羅西書 1:1-23, '
            '詩篇 116:12-19'
        ],
        278: [
            False, '耶利米書 9:17-26, 耶利米書 10, 耶利米書 11:1-17, 歌羅西書 1:24-29, '
            '歌羅西書 2:1-5, 詩篇 117:1-2'
        ],
        279: [
            False, '耶利米書 11:18-23, 耶利米書 12, 耶利米書 13, 歌羅西書 2:6-23, 詩篇 '
            '118:1-16'
        ],
        280: [False, '耶利米書 14, 耶利米書 15, 歌羅西書 3, 歌羅西書 4:1, 箴言 '
              '24:15-22'],
        281: [False, '耶利米書 16, 耶利米書 17, 歌羅西書 4:2-18, 詩篇 118:17-29'],
        282: [
            False,
            '耶利米書 18, 耶利米書 19, 耶利米書 20, 帖撒羅尼迦前書 1, 帖撒羅尼迦前書 2:1-16, 詩篇 119:1-8'
        ],
        283: [
            False,
            '耶利米書 21, 耶利米書 22, 耶利米書 23:1-8, 帖撒羅尼迦前書 2:17-19, 帖撒羅尼迦前書 3, 詩篇 119:9-16'
        ],
        284: [
            False, '耶利米書 23:9-40, 耶利米書 24, 耶利米書 25:1-14, 帖撒羅尼迦前書 4, '
            '箴言 24:23-34'
        ],
        285: [False, '耶利米書 25:15-38, 耶利米書 26, 帖撒羅尼迦前書 5, 詩篇 119:17-24'],
        286: [
            False, '耶利米書 27, 耶利米書 28, 耶利米書 29:1-23, 帖撒羅尼迦後書 1, 詩篇 '
            '119:25-32'
        ],
        287: [
            False, '耶利米書 29:24-32, 耶利米書 30, 耶利米書 31:1-14, 帖撒羅尼迦後書 2, '
            '詩篇 119:33-40'
        ],
        288: [False, '耶利米書 31:15-40, 耶利米書 32:1-25, 帖撒羅尼迦後書 3, 箴言 '
              '25:1-10'],
        289: [
            False, '耶利米書 32:26-44, 耶利米書 33, 耶利米書 34, 提摩太前書 1, 詩篇 '
            '119:41-48'
        ],
        290: [False, '耶利米書 35, 耶利米書 36, 耶利米書 37, 提摩太前書 2, 詩篇 119:49-56'],
        291: [
            False, '耶利米書 38, 耶利米書 39, 耶利米書 40:1-6, 提摩太前書 3, 詩篇 '
            '119:57-64'
        ],
        292: [
            False, '耶利米書 40:7-16, 耶利米書 41, 耶利米書 42, 提摩太前書 4, 箴言 '
            '25:11-20'
        ],
        293: [
            False, '耶利米書 43, 耶利米書 44, 耶利米書 45, 提摩太前書 5, 提摩太前書 6:1-2, '
            '詩篇 119:65-72'
        ],
        294: [False, '耶利米書 46, 耶利米書 47, 提摩太前書 6:3-21, 詩篇 119:73-80'],
        295: [False, '耶利米書 48, 耶利米書 49:1-6, 提摩太後書 1, 詩篇 119:81-88'],
        296: [
            False, '耶利米書 49:7-39, 耶利米書 50:1-10, 提摩太後書 2, 箴言 25:21-28, '
            '箴言 26:1-2'
        ],
        297: [False, '耶利米書 50:11-46, 耶利米書 51:1-14, 提摩太後書 3, 詩篇 119:89-96'],
        298: [False, '耶利米書 51:15-64, 提摩太後書 4, 詩篇 119:97-104'],
        299: [False, '耶利米書 52, 提多書 1, 詩篇 119:105-112'],
        300: [False, '哈巴谷書 1, 哈巴谷書 2, 哈巴谷書 3:1-19, 提多書 2, 箴言 26:3-12'],
        301: [False, '耶利米哀歌 1, 耶利米哀歌 2:1-6, 提多書 3, 詩篇 119:113-120'],
        302: [False, '耶利米哀歌 2:7-27, 耶利米哀歌 3:1-39, 腓利門書 1, 詩篇 '
              '119:121-128'],
        303: [
            False, '耶利米哀歌 3:40-66, 耶利米哀歌 4, 耶利米哀歌 5, 希伯來書 1, 詩篇 '
            '119:129-136'
        ],
        304: [False, '俄巴底亞書 1, 希伯來書 2, 箴言 26:13-22'],
        305: [False, '約珥書 1, 約珥書 2:1-17, 希伯來書 3, 詩篇 119:137-144'],
        306: [False, '約珥書 2:18-32, 約珥書 3, 希伯來書 4:1-13, 詩篇 119:145-152'],
        307: [
            False, '以西結書 1, 以西結書 2, 以西結書 3, 希伯來書 4:14-16, 希伯來書 5:1-10, '
            '詩篇 119:153-160'
        ],
        308: [
            False, '以西結書 4, 以西結書 5, 以西結書 6, 希伯來書 5:11-14, 希伯來書 6:1-12, '
            '箴言 26:23-28, 箴言 27:1-4'
        ],
        309: [
            False, '以西結書 7, 以西結書 8, 以西結書 9, 希伯來書 6:13-20, 希伯來書 7:1-10, '
            '詩篇 119:161-168'
        ],
        310: [
            False, '以西結書 10, 以西結書 11, 以西結書 12, 希伯來書 7:11-28, 詩篇 '
            '119:169-176'
        ],
        311: [False, '以西結書 13, 以西結書 14, 以西結書 15, 希伯來書 8, 詩篇 120:1-7'],
        312: [False, '以西結書 16, 希伯來書 9:1-15, 箴言 27:5-14'],
        313: [False, '以西結書 17, 以西結書 18, 希伯來書 9:16-28, 詩篇 121:1-8'],
        314: [False, '以西結書 19, 以西結書 20:1-44, 希伯來書 10:1-18, 詩篇 122:1-9'],
        315: [
            False, '以西結書 20:45-49, 以西結書 21, 以西結書 22:1-22, 希伯來書 10:19-39, 詩篇 '
            '123:1-4'
        ],
        316: [False, '以西結書 22:23-31, 以西結書 23, 希伯來書 11:1-16, 箴言 27:15-22'],
        317: [False, '以西結書 24, 以西結書 25, 希伯來書 11:17-40, 詩篇 124:1-8'],
        318: [False, '以西結書 26, 以西結書 27, 希伯來書 12:1-13, 詩篇 125:1-5'],
        319: [False, '以西結書 28, 以西結書 29, 希伯來書 12:14-29, 詩篇 126:1-6'],
        320: [False, '以西結書 30, 以西結書 31, 希伯來書 13, 箴言 27:23-27, 箴言 '
              '28:1-6'],
        321: [False, '以西結書 32, 以西結書 33:1-32, 雅各書 1, 詩篇 127:1-5'],
        322: [False, '以西結書 33:21-33, 以西結書 34, 以西結書 35, 雅各書 2, 詩篇 128:1-6'],
        323: [False, '以西結書 36, 以西結書 37, 雅各書 3, 詩篇 129:1-8'],
        324: [False, '以西結書 38, 以西結書 39, 雅各書 4, 箴言 28:7-17'],
        325: [False, '以西結書 40, 雅各書 5, 詩篇 130:1-8'],
        326: [False, '以西結書 41, 以西結書 42, 彼得前書 1, 彼得前書 2:1-3, 詩篇 131:1-3'],
        327: [False, '以西結書 43, 以西結書 44, 彼得前書 2:4-25, 詩篇 132:1-18'],
        328: [False, '以西結書 45, 以西結書 46, 彼得前書 3, 箴言 28:18-28'],
        329: [False, '以西結書 47, 以西結書 48, 彼得前書 4, 詩篇 133:1-3'],
        330: [False, '但以理書 1, 但以理書 2:1-23, 彼得前書 5, 詩篇 134:1-3'],
        331: [False, '但以理書 2:24-49, 但以理書 3:1-12, 彼得後書 1, 詩篇 135:1-12'],
        332: [False, '但以理書 3:13-30, 但以理書 4:1-18, 彼得後書 2, 箴言 29:1-9'],
        333: [False, '但以理書 4:19-37, 但以理書 5:1-16, 彼得後書 3, 詩篇 135:13-21'],
        334: [False, '但以理書 5:17-31, 但以理書 6:1-28, 約翰一書 1, 約翰一書 2, 詩篇 136:1-12'],
        335: [False, '但以理書 7, 但以理書 8:1-14, 約翰一書 2:12-27, 詩篇 136:13-26'],
        336: [
            False, '但以理書 8:15-27, 但以理書 9:1-19, 約翰一書 2:28-29, 約翰一書 3:1-10, 箴言 '
            '29:10-18'
        ],
        337: [
            False,
            '但以理書 9:20-27, 但以理書 10, 但以理書 11:1, 約翰一書 3:11-24, 約翰一書 4:1-6, '
            '詩篇 137:1-9'
        ],
        338: [False, '但以理書 11:2-35, 約翰一書 4:7-21, 詩篇 138:1-8'],
        339: [False, '但以理書 11:36-45, 但以理書 12, 約翰一書 5:1-21, 詩篇 139:1-10'],
        340: [False, '哈該書 1, 哈該書 2:1-23, 約翰二書 1:1-13, 箴言 29:19-27'],
        341: [
            False, '撒迦利亞書 1, 撒迦利亞書 2, 撒迦利亞書 3, 撒迦利亞書 4, 約翰三書 1:1-14, '
            '詩篇 139:11-16'
        ],
        342: [
            False, '撒迦利亞書 5, 撒迦利亞書 6, 撒迦利亞書 7, 撒迦利亞書 8, 猶大書 1:1-25, 詩篇 '
            '139:17-24'
        ],
        343: [False, '撒迦利亞書 9, 撒迦利亞書 10, 撒迦利亞書 11, 啟示錄 1, 詩篇 140:1-5'],
        344: [
            False, '撒迦利亞書 12, 撒迦利亞書 13, 撒迦利亞書 14, 啟示錄 2:1-17, 箴言 '
            '30:1-10'
        ],
        345: [
            False, '以斯帖記 1, 以斯帖記 2:1-18, 啟示錄 2:18-29, 啟示錄 3:1-6, 詩篇 '
            '140:6-13'
        ],
        346: [
            False, '以斯帖記 2:19-23, 以斯帖記 3, 以斯帖記 4, 以斯帖記 5, 啟示錄 3:7-22, 詩篇 '
            '141:1-10'
        ],
        347: [False, '以斯帖記 6, 以斯帖記 7, 以斯帖記 8, 啟示錄 4, 詩篇 142:1-11'],
        348: [False, '以斯帖記 9, 以斯帖記 10, 啟示錄 5, 箴言 30:11-23'],
        349: [False, '瑪拉基書 1, 瑪拉基書 2:1-16, 啟示錄 6, 詩篇 143:1-12'],
        350: [False, '瑪拉基書 2:17, 瑪拉基書 3, 瑪拉基書 4, 啟示錄 7, 詩篇 144:1-8'],
        351: [False, '以斯拉記 1, 以斯拉記 2:1-67, 啟示錄 8, 啟示錄 9:1-12, 詩篇 144:9-15'],
        352: [
            False, '以斯拉記 2:68-70, 以斯拉記 3, 以斯拉記 4:1-5, 啟示錄 9:13-21, 啟示錄 10, '
            '箴言 30:24-33'
        ],
        353: [False, '以斯拉記 4:6-24, 以斯拉記 5, 啟示錄 11, 詩篇 145:1-7'],
        354: [False, '以斯拉記 6, 以斯拉記 7:1-10, 啟示錄 12, 啟示錄 13:1, 詩篇 145:8-13'],
        355: [False, '以斯拉記 7:11-28, 以斯拉記 8:1-14, 啟示錄 13:1-18, 詩篇 145:13-21'],
        356: [False, '以斯拉記 8:15-36, 以斯拉記 9:1-15, 啟示錄 14:1-13, 箴言 31:1-9'],
        357: [False, '以斯拉記 10, 啟示錄 14:14-20, 啟示錄 15, 詩篇 146:1-10'],
        358: [False, '尼希米記 1, 尼希米記 2, 啟示錄 16, 詩篇 147:1-11'],
        359: [False, '尼希米記 3, 尼希米記 4, 啟示錄 17, 詩篇 147:12-20'],
        360: [
            False, '尼希米記 5, 尼希米記 6, 尼希米記 7:1-3, 啟示錄 18:1-17, 箴言 '
            '31:10-20'
        ],
        361: [
            False, '尼希米記 7:4-73, 尼希米記 8, 啟示錄 18:17-24, 啟示錄 19:1-10, '
            '詩篇 148:1-6'
        ],
        362: [False, '尼希米記 9:1-37, 啟示錄 19:11-21, 詩篇 148:7-14'],
        363: [
            False, '尼希米記 9:38, 尼希米記 10, 尼希米記 11:1-21, 啟示錄 20, 詩篇 '
            '149:1-9'
        ],
        364: [False, '尼希米記 11:22-36, 尼希米記 12:1-47, 啟示錄 21, 箴言 31:21-31'],
        365: [False, '尼希米記 13, 啟示錄 22, 詩篇 150:1-6'],
    }

    translation = (
        "聖經閱讀計劃",
        "今天是 ",
        "搜索:",
        "聖經視窗頁籤中開啓",
        "隱藏已經閱讀的經文",
        "顯示已經閱讀的經文",
        "重新設置",
        "儲存閱讀進度",
        "第",
        "天",
        "您的閱讀進度儲存在下面這個檔案:",
        "沒法把您的閱讀進度儲存在您現在使用的電腦上!",
    )

    def __init__(self, parent):
        super().__init__()
        self.parent = parent
        # set title
        self.setWindowTitle(self.translation[0])
        self.setMinimumSize(830, 500)
        # set variables
        self.setupVariables()
        # setup interface
        self.setupUI()

    def setupVariables(self):
        import copy, os
        from datetime import date
        self.today = date.today()
        self.todayNo = int(format(self.today, '%j'))
        if self.todayNo > 365:
            self.todayNo = 365
        self.progressFile = os.path.join(os.getcwd(), "plugins", "menu",
                                         "{0}.txt".format(self.translation[0]))
        if os.path.isfile(self.progressFile):
            from ast import literal_eval
            with open(self.progressFile, "r") as fileObj:
                self.plan = literal_eval(fileObj.read())
        else:
            self.plan = copy.deepcopy(self.template)
        self.hideCheckedItems = False

    def setupUI(self):
        from qtpy.QtGui import QStandardItemModel
        from qtpy.QtWidgets import (QPushButton, QLabel, QListView,
                                    QAbstractItemView, QHBoxLayout,
                                    QVBoxLayout, QLineEdit)

        mainLayout = QVBoxLayout()

        readingListLayout = QVBoxLayout()

        readingListLayout.addWidget(QLabel(self.translation[0]))
        readingListLayout.addWidget(
            QLabel("{0}{1}".format(self.translation[1], self.today)))

        filterLayout = QHBoxLayout()
        filterLayout.addWidget(QLabel(self.translation[2]))
        self.filterEntry = QLineEdit()
        self.filterEntry.textChanged.connect(self.resetItems)
        filterLayout.addWidget(self.filterEntry)
        readingListLayout.addLayout(filterLayout)

        self.readingList = QListView()
        self.readingList.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.readingListModel = QStandardItemModel(self.readingList)
        self.readingList.setModel(self.readingListModel)
        self.resetItems()
        self.readingListModel.itemChanged.connect(self.itemChanged)
        #print(self.readingList.currentIndex().row())
        #self.readingList.selectionModel().selectionChanged.connect(self.function)
        readingListLayout.addWidget(self.readingList)

        buttonsLayout = QHBoxLayout()

        button = QPushButton(self.translation[3])
        button.clicked.connect(self.openInTabs)
        buttonsLayout.addWidget(button)

        self.hideShowButton = QPushButton(self.translation[4])
        self.hideShowButton.clicked.connect(self.hideShowCheckedItems)
        buttonsLayout.addWidget(self.hideShowButton)

        button = QPushButton(self.translation[6])
        button.clicked.connect(self.resetAllItems)
        buttonsLayout.addWidget(button)

        button = QPushButton(self.translation[7])
        button.clicked.connect(self.saveProgress)
        buttonsLayout.addWidget(button)

        mainLayout.addLayout(readingListLayout)
        mainLayout.addLayout(buttonsLayout)

        self.setLayout(mainLayout)

    def itemChanged(self, standardItem):
        from qtpy.QtCore import Qt
        key = int(standardItem.text().split(".")[0])
        if standardItem.checkState() is Qt.CheckState.Checked:
            self.plan[key][0] = True
        elif standardItem.checkState() is Qt.CheckState.Unchecked:
            self.plan[key][0] = False
        if self.hideCheckedItems:
            self.resetItems()

    def resetItems(self):
        from qtpy.QtGui import QStandardItem
        from qtpy.QtCore import Qt
        # Empty the model before reset
        self.readingListModel.clear()
        # Reset
        index = 0
        todayIndex = None
        filterEntry = self.filterEntry.text()
        for key, value in self.plan.items():
            checked, passages = value
            if not (self.hideCheckedItems and checked) and (
                    filterEntry == "" or
                (filterEntry != ""
                 and filterEntry.lower() in passages.lower())):
                item = QStandardItem("{0}. {1}".format(key, passages))
                item.setToolTip("{0}{1}{2}".format(self.translation[8], key,
                                                   self.translation[9]))
                if key == self.todayNo:
                    todayIndex = index
                item.setCheckable(True)
                item.setCheckState(Qt.CheckState.Checked if checked else Qt.
                                   CheckState.Unchecked)
                self.readingListModel.appendRow(item)
                index += 1
        if todayIndex is not None:
            self.readingList.setCurrentIndex(
                self.readingListModel.index(todayIndex, 0))

    def hideShowCheckedItems(self):
        self.hideCheckedItems = not self.hideCheckedItems
        self.resetItems()
        self.hideShowButton.setText(self.translation[5] if self.
                                    hideCheckedItems else self.translation[4])

    def resetAllItems(self):
        import copy
        self.plan = copy.deepcopy(self.template)
        self.resetItems()

    def translateIntoChinese(self):
        import copy, pprint
        from BibleBooks import BibleBooks
        plan = copy.deepcopy(self.template)
        filePath = "{0}_zh".format(self.progressFile)
        with open(filePath, "w", encoding="utf-8") as fileObj:
            fileObj.write(pprint.pformat(plan))
        with open(filePath, "r") as fileObj:
            text = fileObj.read()
        translateDict = {}
        bookNames = []
        for key, value in BibleBooks.eng.items():
            bookName = value[-1]
            bookNames.append(bookName)
            translateDict[bookName] = BibleBooks.sc[key][-1]
        bookNames = sorted(bookNames, key=len, reverse=True)
        #print(bookNames)
        for name in bookNames:
            text = text.replace(name, translateDict[name])
        text = text.replace("Psalm", "诗篇")
        with open(filePath, "w", encoding="utf-8") as fileObj:
            fileObj.write(text)

    def saveProgress(self):
        import pprint
        from qtpy.QtWidgets import QMessageBox
        try:
            with open(self.progressFile, "w", encoding="utf-8") as fileObj:
                fileObj.write(pprint.pformat(self.plan))
            message = "{0}\n'{1}'".format(self.translation[10],
                                          self.progressFile)
        except:
            message = self.translation[11]
        QMessageBox.information(self, self.translation[0], message)

    def openInTabs(self):
        dayNo = self.readingList.currentIndex().row() + 1
        todayReading = self.plan[dayNo][-1].split(", ")
        openBibleWindowContentOnNextTab = config.openBibleWindowContentOnNextTab
        config.openBibleWindowContentOnNextTab = True
        for reading in todayReading:
            command = "MAIN:::{0}".format(reading)
            self.parent.runTextCommand(command)
        config.openBibleWindowContentOnNextTab = openBibleWindowContentOnNextTab
        self.close()
示例#6
0
class PreprocessWidget(QSplitter):
    def __init__(self, headermodel, selectionmodel):
        super(PreprocessWidget, self).__init__()
        self.headermodel = headermodel
        self.mapselectmodel = selectionmodel
        self.selectMapidx = 0
        self.resultDict = {}
        self.isBatchProcessOn = False
        self.out = None
        self.dfDict = None
        self.reportList = ['preprocess_method', 'wav_anchor', 'interp_method', 'w_regions']
        self.arrayList = ['kohlerDebased', 'kohlerBaseline', 'rubberDebased', 'deriv2_kohler', 'deriv2_rubber']
        self.mousePosList = []

        # split between spectrum parameters and viewwindow, vertical split
        self.params_and_specview = QSplitter()
        self.params_and_specview.setOrientation(Qt.Vertical)
        # split between buttons and parameters
        self.buttons_and_params = QSplitter()
        self.buttons_and_params.setOrientation(Qt.Horizontal)
        # split between speclist and report
        self.speclist_and_report = QSplitter()
        self.speclist_and_report.setOrientation(Qt.Vertical)

        # buttons layout
        self.buttons = QWidget()
        self.buttonlayout = QGridLayout()
        self.buttons.setLayout(self.buttonlayout)
        # set up buttons
        self.fontSize = 12
        font = QFont("Helvetica [Cronyx]", self.fontSize)
        self.loadBtn = QPushButton()
        self.loadBtn.setText('Load spectra')
        self.loadBtn.setFont(font)
        self.removeBtn = QPushButton()
        self.removeBtn.setText('Remove spectrum')
        self.removeBtn.setFont(font)
        self.normBox = QComboBox()
        self.normBox.addItems(['Raw spectrum',
                               'Kohler EMSC baseline',
                               'Rubberband baseline',
                               'Kohler EMSC + 2nd derivative',
                               'Rubberband + 2nd derivative',
                               ])
        self.normBox.setFont(font)
        self.batchBtn = QPushButton()
        self.batchBtn.setText('Batch process')
        self.batchBtn.setFont(font)
        self.saveResultBox = QComboBox()
        self.saveResultBox.addItems(['Save kohler',
                                     'Save kohler baseline',
                                     'Save rubberband',
                                     'Save kohler 2nd derivative',
                                     'Save rubberband 2nd derivative',
                                     'Save all',
                                     ])
        self.saveResultBox.setFont(font)
        # add all buttons
        self.buttonlayout.addWidget(self.loadBtn)
        self.buttonlayout.addWidget(self.removeBtn)
        self.buttonlayout.addWidget(self.normBox)
        self.buttonlayout.addWidget(self.batchBtn)
        self.buttonlayout.addWidget(self.saveResultBox)
        # define report
        self.reportWidget = QWidget()
        self.reportWidget.setLayout(QVBoxLayout())
        self.infoBox = QTextEdit()
        reportTitle = QLabel('Preprocess results')
        reportTitle.setFont(font)
        self.reportWidget.layout().addWidget(reportTitle)
        self.reportWidget.layout().addWidget(self.infoBox)
        # spectrum list view
        self.specItemModel = QStandardItemModel()
        self.specSelectModel = QItemSelectionModel(self.specItemModel)
        self.speclistview = QListView()
        self.speclistview.setModel(self.specItemModel)
        self.speclistview.setSelectionModel(self.specSelectModel)
        # add title to list view
        self.specListWidget = QWidget()
        self.listLayout = QVBoxLayout()
        self.specListWidget.setLayout(self.listLayout)
        specListTitle = QLabel('Spectrum List')
        specListTitle.setFont(font)
        self.listLayout.addWidget(specListTitle)
        self.listLayout.addWidget(self.speclistview)

        # spectrum plot
        self.rawSpectra = baselinePlotWidget()
        self.resultSpectra = baselinePlotWidget()
        # ParameterTree
        self.parametertree = PreprocessParameters()
        self.parameter = self.parametertree.parameter
        self.processArgs = self.parametertree.processArgs
        self.argMap = self.parametertree.argMap

        # assemble widgets
        self.buttons_and_params.addWidget(self.parametertree)
        self.buttons_and_params.addWidget(self.buttons)
        self.buttons_and_params.setSizes([1000, 100])
        self.params_and_specview.addWidget(self.buttons_and_params)
        self.params_and_specview.addWidget(self.rawSpectra)
        self.params_and_specview.addWidget(self.resultSpectra)
        self.params_and_specview.setSizes([150, 50, 50])
        self.speclist_and_report.addWidget(self.specListWidget)
        self.speclist_and_report.addWidget(self.reportWidget)
        self.speclist_and_report.setSizes([150, 100])
        self.addWidget(self.params_and_specview)
        self.addWidget(self.speclist_and_report)
        self.setSizes([1000, 200])

        # Connect signals
        self.loadBtn.clicked.connect(self.loadData)
        self.removeBtn.clicked.connect(self.removeSpec)
        self.batchBtn.clicked.connect(self.batchProcess)
        self.saveResultBox.currentIndexChanged.connect(self.saveResults)
        self.specSelectModel.selectionChanged.connect(self.updateSpecPlot)
        self.normBox.currentIndexChanged.connect(self.updateSpecPlot)
        self.parametertree.sigParamChanged.connect(self.updateSpecPlot)
        self.rawSpectra.scene().sigMouseClicked.connect(self.setAnchors)
        self.parameter.child('Preprocess method').sigValueChanged.connect(self.updateMethod)

    def setHeader(self, field: str):
        self.headers = [self.headermodel.item(i).header for i in range(self.headermodel.rowCount())]
        self.field = field
        self.wavenumberList = []
        self.rc2indList = []
        self.ind2rcList = []
        self.pathList = []
        self.dataSets = []

        # get wavenumbers, rc2ind
        for header in self.headers:
            dataEvent = next(header.events(fields=[field]))
            self.wavenumberList.append(dataEvent['wavenumbers'])
            self.rc2indList.append(dataEvent['rc_index'])
            self.ind2rcList.append(dataEvent['index_rc'])
            self.pathList.append(dataEvent['path'])
            # get raw spectra
            data = None
            try:  # spectra datasets
                data = header.meta_array('spectra')
            except IndexError:
                msg.logMessage('Header object contained no frames with field ''{field}''.', msg.ERROR)
            if data is not None:
                self.dataSets.append(data)

    def isMapOpen(self):
        if not self.mapselectmodel.selectedIndexes():  # no map is open
            return False
        else:
            self.selectMapidx = self.mapselectmodel.selectedIndexes()[0].row()
            return True

    def setAnchors(self, event):
        # get current map idx and selected spectrum idx
        specidx = self.getCurrentSpecid()
        plotChoice = self.normBox.currentIndex()
        if (not self.isMapOpen()) or (self.specItemModel.rowCount() == 0) or (specidx is None) or (plotChoice not in [2, 4]):
            return

        pos = event.pos()
        button = event.button()
        parser = Preprocessor(self.wavenumberList[self.selectMapidx], self.dataSets[self.selectMapidx][specidx])
        parser.parse_anchors(self.parameter['Anchor points'])
        anchor_low, anchor_high = parser.wav_anchor[0], parser.wav_anchor[-1]
        if self.rawSpectra.getViewBox().sceneBoundingRect().contains(pos):
            mousePoint = self.rawSpectra.getViewBox().mapToView(pos)
            x = mousePoint.x()
            if anchor_low < x < anchor_high:
                if button == Qt.LeftButton:# left click, add point to mousePosList
                    self.mousePosList.append(x)
                elif (button == Qt.MidButton) and self.mousePosList :# right click, remove last point from mousePosList
                    self.mousePosList.pop()
                # set anchors list
                anchors = [anchor_low] + sorted(self.mousePosList) + [anchor_high]
                txt = ', '.join([str(int(round(x))) for x in anchors])
                self.parameter.child('Anchor points').setValue(txt)

    def getCurrentSpecid(self):
        # get selected spectrum idx
        specidx = None  # default value
        if self.specSelectModel.selectedIndexes():
            selectedSpecRow = self.specSelectModel.selectedIndexes()[0].row()
            currentSpecItem = self.specItemModel.item(selectedSpecRow)
            specidx = currentSpecItem.idx
        return specidx

    def updateMethod(self):
        if self.parameter["Preprocess method"] == 'Kohler_EMSC':
            self.normBox.setCurrentIndex(1)
        else:
            self.normBox.setCurrentIndex(2)

    def updateSpecPlot(self):
        # get current map idx and selected spectrum idx
        specidx = self.getCurrentSpecid()
        if not self.isMapOpen():
            return
        elif self.specItemModel.rowCount() == 0:
            MsgBox('No spectrum is loaded.\nPlease click "Load spectra" to import data.')
            return
        elif specidx is None:
            return

        # get plotchoice
        plotChoice = self.normBox.currentIndex()

        # create Preprocessor object
        self.out = Preprocessor(self.wavenumberList[self.selectMapidx], self.dataSets[self.selectMapidx][specidx])
        baselineOK = self.out.rubber_band(**self.processArgs) and self.out.kohler(**self.processArgs)

        if not baselineOK:
            return

        # make results report
        if plotChoice != 0:
            self.getReport(self.out, plotChoice)

        # if not batch processing, show plots
        if not self.isBatchProcessOn:
            # clean up plots
            self.rawSpectra.clearAll()
            self.resultSpectra.clearAll()
            if plotChoice == 0:  # plot raw spectrum
                self.infoBox.setText('')  # clear txt
                self.rawSpectra.plotBase(self.out, plotType='raw')
            elif plotChoice == 1:  # plot raw, kohler
                self.rawSpectra.plotBase(self.out, plotType='kohler_base')
                self.resultSpectra.plotBase(self.out, plotType='kohler')
            elif plotChoice == 2:  # plot raw, rubberband
                self.rawSpectra.plotBase(self.out, plotType='rubber_base')
                self.resultSpectra.plotBase(self.out, plotType='rubberband')
            elif plotChoice == 3:  # plot raw, kohler 2nd derivative
                self.rawSpectra.plotBase(self.out, plotType='kohler_base')
                self.resultSpectra.plotBase(self.out, plotType='deriv2_kohler')
            elif plotChoice == 4:  # plot raw, rubberband 2nd derivative
                self.rawSpectra.plotBase(self.out, plotType='rubber_base')
                self.resultSpectra.plotBase(self.out, plotType='deriv2_rubberband')

            if plotChoice in [1, 3]:
                self.parameter.child('Preprocess method').setValue('Kohler_EMSC', blockSignal=self.updateMethod)
            elif plotChoice in [2, 4]:
                self.parameter.child('Preprocess method').setValue('Rubberband', blockSignal=self.updateMethod)

    def getReport(self, output, plotChoice):
        resultTxt = ''
        # get baseline results
        reportList = self.reportList.copy()
        if plotChoice in [2, 4]:
            reportList = self.reportList[:-1]
            output.preprocess_method = 'rubberband'
        elif plotChoice in [1, 3]:
            reportList = [self.reportList[0], self.reportList[-1]]
            output.preprocess_method = 'kohler'

        for item in dir(output):
            if item in reportList:
                if item == 'wav_anchor':
                    val = getattr(output, item)
                    printFormat = ('{:.2f}, ' * len(val))[:-1]
                    resultTxt += item + ': ' + printFormat.format(*val) + '\n'
                else:
                    resultTxt += item + ': ' + str(getattr(output, item)) + '\n'
            if (item in self.arrayList) or (item in self.reportList):
                self.resultDict[item] = getattr(output, item)

        # send text to report info box
        self.infoBox.setText(resultTxt)

    def loadData(self):
        # get current map idx
        if not self.isMapOpen():
            return
        # pass the selected map data to plotwidget
        self.rawSpectra.setHeader(self.headers[self.selectMapidx], 'spectra')
        currentMapItem = self.headermodel.item(self.selectMapidx)
        rc2ind = self.rc2indList[self.selectMapidx]
        # get current map name
        mapName = currentMapItem.data(0)
        # get current selected pixels
        pixelCoord = currentMapItem.selectedPixels
        # get selected specIds
        spectraIds = []
        if currentMapItem.selectedPixels is None:  # select all
            spectraIds = list(range(len(rc2ind)))
        else:
            for i in range(len(pixelCoord)):
                row_col = tuple(pixelCoord[i])
                spectraIds.append(rc2ind[row_col])
            spectraIds = sorted(spectraIds)
        # add specitem model
        self.specItemModel.clear()
        for idx in spectraIds:
            item = QStandardItem(mapName + '# ' + str(idx))
            item.idx = idx
            self.specItemModel.appendRow(item)

    def removeSpec(self):
        # get current selectedSpecRow
        if self.specSelectModel.selectedIndexes():
            selectedSpecRow = self.specSelectModel.selectedIndexes()[0].row()
            self.specSelectModel.blockSignals(True)
            self.specItemModel.removeRow(selectedSpecRow)
            self.specSelectModel.blockSignals(False)
            # clean up plots
            self.rawSpectra.clearAll()
            self.resultSpectra.clearAll()
            self.infoBox.setText('')

    def cleanUp(self):
        self.specItemModel.clear()
        self.rawSpectra.clearAll()
        self.resultSpectra.clearAll()
        self.infoBox.setText('')
        self.mousePosList = []
        self.normBox.setCurrentIndex(0)

    def batchProcess(self):
        # get current map idx
        if not self.isMapOpen():
            return
        elif self.specItemModel.rowCount() == 0:
            MsgBox('No spectrum is loaded.\nPlease click "Load spectra" to import data.')
            return
        # check if baseline fit OK
        if self.out is None:
            self.out = Preprocessor(self.wavenumberList[self.selectMapidx], self.dataSets[self.selectMapidx][0])

        # get plotchoice
        plotChoice = self.normBox.currentIndex()
        if plotChoice != 0:
            # calculate rubberband and kohler baseline
            baselineOK = self.out.rubber_band(**self.processArgs) and self.out.kohler(**self.processArgs)
        else:
            MsgBox('Plot type is "Raw spectrum".\nPlease change plot type to "Kohler" or "Rubberband".')
            return
        if not baselineOK:
            return

        # notice to user
        userMsg = YesNoDialog(f'Ready to batch process selected spectra.\nDo you want to continue?')
        userChoice = userMsg.choice()
        if userChoice == QMessageBox.No:  # user choose to stop
            return

        self.isBatchProcessOn = True

        # init resultSetsDict, paramsDict
        self.resultSetsDict = {}
        self.paramsDict = {}
        self.paramsDict['specID'] = []
        self.paramsDict['row_column'] = []
        ind2rc = self.ind2rcList[self.selectMapidx]
        energy = self.out.energy
        n_energy = len(energy)
        for item in self.arrayList:
            self.resultSetsDict[item] = np.empty((0, n_energy))
        for item in self.reportList:
            self.paramsDict[item] = []
        # batch process begins
        n_spectra = self.specItemModel.rowCount()
        for i in range(n_spectra):
            msg.showMessage(f'Processing {i + 1}/{n_spectra} spectra')
            # select each spec and collect results
            self.specSelectModel.select(self.specItemModel.index(i, 0), QItemSelectionModel.ClearAndSelect)
            # get spec idx
            currentSpecItem = self.specItemModel.item(i)
            self.paramsDict['specID'].append(currentSpecItem.idx)
            self.paramsDict['row_column'].append(ind2rc[currentSpecItem.idx])
            # append all results into a single array/list
            for item in self.arrayList:
                self.resultSetsDict[item] = np.append(self.resultSetsDict[item], self.resultDict[item].reshape(1, -1),
                                                      axis=0)
            for item in self.reportList:
                self.paramsDict[item].append(self.resultDict[item])

        # result collection completed. convert paramsDict to df
        self.dfDict = {}
        self.dfDict['param'] = pd.DataFrame(self.paramsDict).set_index('specID')
        for item in self.arrayList:
            # convert resultSetsDict to df
            self.dfDict[item] = pd.DataFrame(self.resultSetsDict[item], columns=energy.tolist(),
                                        index=self.paramsDict['specID'])

        # batch process completed
        self.isBatchProcessOn = False
        msg.showMessage(f'Batch processing is completed! Saving results to csv files.')
        #  save df to files
        self.saveResults()

    def saveResults(self):
        if self.dfDict is None:
            return
        filePath = self.pathList[self.selectMapidx]
        energy = self.out.energy
        saveDataChoice = self.saveResultBox.currentIndex()
        if saveDataChoice != 5:  # save a single result
            saveDataType = self.arrayList[saveDataChoice]
            dirName, csvName, h5Name = self.saveToFiles(energy, self.dfDict, filePath, saveDataType)
            if h5Name is None:
                MsgBox(f'Processed data was saved as csv file at: \n{dirName + csvName}')
            else:
                MsgBox(
                    f'Processed data was saved as: \n\ncsv file at: {dirName + csvName} and \n\nHDF5 file at: {dirName + h5Name}')
        else:  # save all results
            csvList = []
            h5List = []
            for saveDataType in self.arrayList:
                dirName, csvName, h5Name = self.saveToFiles(energy, self.dfDict, filePath, saveDataType)
                csvList.append(csvName)
                h5List.append(h5Name)

            allcsvName = (', ').join(csvList)
            if h5Name is None:
                MsgBox(f'Processed data was saved as csv files at: \n{dirName + allcsvName}')
            else:
                allh5Name = (', ').join(h5List)
                MsgBox(
                    f'Processed data was saved as: \n\ncsv files at: {dirName + allcsvName} and \n\nHDF5 files at: {dirName + allh5Name}')

        # save parameter
        xlsName = csvName[:-4] + '_param.xlsx'
        self.dfDict['param'].to_excel(dirName + xlsName)

    def saveToFiles(self, energy, dfDict, filePath, saveDataType):

        ind2rc = self.ind2rcList[self.selectMapidx]
        n_spectra = self.specItemModel.rowCount()

        # get dirname and old filename
        dirName = os.path.dirname(filePath)
        dirName += '/'
        oldFileName = os.path.basename(filePath)

        # save dataFrames to csv file
        csvName = oldFileName[:-3] + '_' + saveDataType + '.csv'
        dfDict[saveDataType].to_csv(dirName + csvName)

        # if a full map is processed, also save results to a h5 file
        h5Name = None
        if n_spectra == len(ind2rc):
            fullMap = ir_map(filename=filePath)
            fullMap.add_image_cube()
            fullMap.wavenumbers = energy
            fullMap.N_w = len(energy)
            fullMap.data = np.zeros((fullMap.data.shape[0], fullMap.N_w))
            fullMap.imageCube = np.zeros((fullMap.imageCube.shape[0], fullMap.imageCube.shape[1], fullMap.N_w))
            for i in self.paramsDict['specID']:
                fullMap.data[i, :] = self.resultSetsDict[saveDataType][i, :]
                row, col = ind2rc[i]
                fullMap.imageCube[row, col, :] = fullMap.data[i, :] = self.resultSetsDict[saveDataType][i, :]
            # save data as hdf5
            h5Name = oldFileName[:-3] + '_' + saveDataType + '.h5'
            fullMap.write_as_hdf5(dirName + h5Name)

        return dirName, csvName, h5Name
示例#7
0
文件: main.py 项目: lnls-sirius/hla
class LogTable(QTreeView, PyDMWidget):
    """Log Table."""

    updated = Signal()

    def __init__(self, parent=None, channels=list(), label2width=dict(),
                 is_status=False):
        # QTableView.__init__(self, parent)
        QTreeView.__init__(self, parent)
        PyDMWidget.__init__(self)

        # setup table
        self._is_status = is_status
        self._date_fmt = ' %Y/%m/%d '
        self._time_fmt = ' %H:%M:%S '
        self.headerLabels = label2width.keys()
        self._model = QStandardItemModel()
        self._model.setHorizontalHeaderLabels(self.headerLabels)
        self.setModel(self._model)
        self.setUniformRowHeights(True)
        self.setHeader(QHeaderView(Qt.Horizontal))
        for idx, width in enumerate(label2width.values()):
            self.header().resizeSection(idx, width)
        self.header().resizeSections(QHeaderView.Fixed)
        self.header().setStretchLastSection(True)
        self.setSortingEnabled(True)
        self.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.setItemDelegateForColumn(2, LogItemDelegate(self))
        self.setSelectionBehavior(QAbstractItemView.SelectItems)
        self.setSelectionMode(QAbstractItemView.SingleSelection)
        self.setStyleSheet("gridline-color: #ffffff;")

        # set channels
        self.address2conn = dict()
        self.address2channels = dict()
        for address in channels:
            self.address2conn[address] = False
            channel = SiriusConnectionSignal(
                address=address,
                connection_slot=self.connection_changed,
                value_slot=self.value_changed,
                severity_slot=self.alarm_severity_changed)
            channel.connect()
            self.address2channels[address] = channel
            self._channels.append(channel)

    @Slot(bool)
    def connection_changed(self, conn):
        """Reimplement connection_changed to handle all channels."""
        address = self.sender().address
        self.address2conn[address] = conn
        allconn = True
        for conn in self.address2conn.values():
            allconn &= conn
        self.setState(allconn)
        self._connected = allconn

    def add_log_slot(self, updated):
        new_value = self._get_newitem_data(updated)
        if not new_value:
            return
        self.add_log(new_value)

    def add_log(self, new_value):
        if self._is_status:
            self.remove_log(new_value)

        datetime_now = _datetime.now()
        item_data = [QStandardItem(text) for text in (
            datetime_now.date().strftime(self._date_fmt),
            datetime_now.time().strftime(self._time_fmt),
            new_value['logtype'], new_value['psname'],
            new_value['propty'], new_value['value'])]
        for item in item_data:
            item.setTextAlignment(Qt.AlignCenter)

        self._model.insertRow(0, item_data)
        if self._model.rowCount() > 10000:
            self._model.removeRow(self._model.rowCount()-1)
        self.updated.emit()

    def remove_log_slot(self, updated):
        new_value = self._get_newitem_data(updated)
        if not new_value:
            return
        self.remove_log(new_value)

    def remove_log(self, new_value):
        for row in range(self._model.rowCount()):
            logtype = self._model.data(self._model.index(row, 2))
            if logtype != new_value['logtype']:
                continue
            psname = self._model.data(self._model.index(row, 3))
            if psname != new_value['psname']:
                continue
            propty = self._model.data(self._model.index(row, 4))
            if propty != new_value['propty']:
                continue
            self._model.removeRow(row)
        self.updated.emit()

    def alarm_severity_changed(self, new_alarm_severity):
        """Reimplement alarm_severity_changed."""
        if self.sender():
            pv_diff = _PVName(self.sender().address)
            val_diff = self.address2channels[pv_diff].value

            pv_opmd = pv_diff.substitute(
                propty_name='OpMode', propty_suffix='Sts')
            val_opmd = self.address2channels[pv_opmd].value
            is_slowref = val_opmd == _PSConst.States.SlowRef

            new_value = {'logtype': 'WARN', 'psname': pv_diff.device_name,
                         'propty': pv_diff.propty_name, 'value': str(val_diff)}
            if new_alarm_severity in [_Sev.MINOR_ALARM, _Sev.MAJOR_ALARM] and \
                    is_slowref:
                self.add_log(new_value)
            elif self._is_status:
                self.remove_log(new_value)

            super().alarm_severity_changed(new_alarm_severity)

    def _get_newitem_data(self, updated):
        pv, value = updated
        pv = _PVName(pv)
        if isinstance(value, _np.ndarray):
            _log.warning('PSDiag window received a numpy array to ' +
                         pv+' ('+str(value)+')!')
            return

        if value is None:
            return
        if 'conn' in self.sender().objectName():
            str_value = 'disconnected'
            logtype = 'DISCONN'
        elif pv.propty_name == 'PwrState':
            str_value = _PSEnums.PWRSTATE_STS[value]
            logtype = 'ERR'
        elif pv.propty_name == 'OpMode':
            str_value = _PSEnums.STATES[value]
            logtype = 'WARN'
        else:
            str_value = str(value)
            logtype = 'ERR'

        return {'logtype': logtype,
                'psname': pv.device_name,
                'propty': '' if logtype == 'DISCONN' else pv.propty_name,
                'value': str_value}

    def mouseDoubleClickEvent(self, ev):
        """Trigger open PS detail window."""
        idx = self.selectedIndexes()
        text = self._model.data(self._model.index(idx[0].row(), 3))
        text = _PVName(text)
        if text.dis == 'PS':
            _run_newprocess(['sirius-hla-as-ps-detail.py', text])
        elif text.dis == 'PU':
            _run_newprocess(['sirius-hla-as-pu-detail.py', text])
        super().mouseDoubleClickEvent(ev)