├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug-issue-report.md │ ├── feature-request.md │ └── question.md ├── options_advanced.png ├── options_general.png ├── options_macros.png └── ui_main.png ├── .gitignore ├── LICENSE ├── README.md ├── __init__.py ├── meta.json ├── res ├── img │ ├── add_icon.svg │ ├── anki_like.png │ ├── chevron_down.svg │ ├── code_icon.svg │ ├── heart_icon.svg │ ├── horiz_lines.svg │ ├── kofilogo_blue.PNG │ ├── patreon.png │ ├── remove_icon.svg │ ├── stats_icon.svg │ ├── vert_grip.svg │ └── vert_lines.svg └── ui │ ├── Qt5 │ ├── cell_item.py │ ├── macro_dialog.py │ └── options_dialog.py │ ├── Qt6 │ ├── cell_item.py │ ├── macro_dialog.py │ └── options_dialog.py │ ├── cell_item.ui │ ├── forms.py │ ├── macro_dialog.ui │ └── options_dialog.ui └── src ├── config.py ├── consts.py ├── options.py ├── overview.py └── toolbar.py /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: iamjustkoi 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: iamjustkoi 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 13 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-issue-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug/Issue Report 3 | about: Create a report to help improve the add-on 4 | title: '' 5 | labels: issue 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Bug/Issue Description** 11 | A short description of the issue. 12 | 13 | **To Reproduce** 14 | Steps to reproduce: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Screenshots/Visuals** 21 | If necessary/applicable, add some screenshots to help explain the issue. 22 | 23 | **System/Debug Info** 24 | You can get this from Anki in Help -> About -> Copy Debug Info, 25 | 26 | **Additional Context** 27 | Any other context that might seem important. 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature Request 3 | about: Ask about implementing a change or idea for the add-on 4 | title: '' 5 | labels: feature 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Potential Issues** 11 | Any issues you've run into that could be resolved. 12 | 13 | **Feature Description** 14 | Description of the feature and how you might want it to work. 15 | 16 | **Alternatives Considered** 17 | Any other alternatives or different features you might've also considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request you think might be useful. 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Question 3 | about: Ask about the add-on 4 | title: '' 5 | labels: question 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | -------------------------------------------------------------------------------- /.github/options_advanced.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamjustkoi/StudyTimeStats/40753916b59d1664950f0bef2414fd7900edad92/.github/options_advanced.png -------------------------------------------------------------------------------- /.github/options_general.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamjustkoi/StudyTimeStats/40753916b59d1664950f0bef2414fd7900edad92/.github/options_general.png -------------------------------------------------------------------------------- /.github/options_macros.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamjustkoi/StudyTimeStats/40753916b59d1664950f0bef2414fd7900edad92/.github/options_macros.png -------------------------------------------------------------------------------- /.github/ui_main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamjustkoi/StudyTimeStats/40753916b59d1664950f0bef2414fd7900edad92/.github/ui_main.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea/* 2 | /.ignored/ 3 | /.build/ 4 | manifest.json 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022-2023 JustKoi (iamjustkoi) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #

Study Time Stats

2 | 3 |

Add some customizable study time and review count statistics to Anki's main window!

4 |

5 | 6 | ## Installation 7 | 8 | Install from [Anki-Web](https://ankiweb.net/shared/info/1247171202) 9 | Or through Anki via: Tools -> Add-ons -> Get Add-ons... 10 | > 1247171202 11 | 12 | ## Options 13 | 14 | ### General 15 | 16 | Customize the style, column count, range, text, code, and output for all rendered statistics! 17 |

18 | 19 | ### Advanced 20 | 21 | Exclude decks, change time formats, and other miscellaneous options! 22 |

23 | 24 | ## Macros 25 | 26 | Custom text macros can be used for all stat outputs! 27 | 28 | These can be accessed via the "Add Text Macro" button (or the little "+" to the right of the text inputs). 29 |

30 | 31 | #### Bugs/Issues: 32 | 33 | Please post any issues/feedback you might have over the project's 34 | on [GitHub](https://github.com/iamjustkoi/StudyTimeStats/issues). 35 |

36 | 37 | Wish you the best! -koi 38 | 39 | MIT License ©2022-2023 JustKoi (iamjustkoi) 40 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- 1 | # MIT License: Copyright (c) 2022-2023 JustKoi (iamjustkoi) 2 | # Full license text available in the "LICENSE" file, packaged with the add-on. 3 | 4 | """ 5 | Shows total study time and a ranged amount of study time in Anki's main window. 6 | """ 7 | from .src import overview 8 | from .src import toolbar 9 | 10 | 11 | def initialize(): 12 | """ 13 | Initializer for the add-on. Called at the start for finer execution order and a bit of readability. 14 | """ 15 | overview.build_hooks() 16 | toolbar.build_toolbar_actions() 17 | 18 | 19 | initialize() 20 | -------------------------------------------------------------------------------- /meta.json: -------------------------------------------------------------------------------- 1 | {"name": "Study Time Stats", "branch_index": 0} -------------------------------------------------------------------------------- /res/img/add_icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 8 | 9 | -------------------------------------------------------------------------------- /res/img/anki_like.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamjustkoi/StudyTimeStats/40753916b59d1664950f0bef2414fd7900edad92/res/img/anki_like.png -------------------------------------------------------------------------------- /res/img/chevron_down.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /res/img/code_icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 12 | 13 | -------------------------------------------------------------------------------- /res/img/heart_icon.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /res/img/horiz_lines.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 9 | 10 | -------------------------------------------------------------------------------- /res/img/kofilogo_blue.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamjustkoi/StudyTimeStats/40753916b59d1664950f0bef2414fd7900edad92/res/img/kofilogo_blue.PNG -------------------------------------------------------------------------------- /res/img/patreon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamjustkoi/StudyTimeStats/40753916b59d1664950f0bef2414fd7900edad92/res/img/patreon.png -------------------------------------------------------------------------------- /res/img/remove_icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 10 | 11 | -------------------------------------------------------------------------------- /res/img/stats_icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 10 | 11 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /res/img/vert_grip.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /res/img/vert_lines.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /res/ui/Qt5/macro_dialog.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file 'macro_dialog.ui' 4 | # 5 | # Created by: PyQt6 UI code generator 5.15.7 6 | # 7 | # WARNING: Any manual changes made to this file will be lost when pyuic5 is 8 | # run again. Do not edit this file unless you know what you are doing. 9 | 10 | 11 | from PyQt5 import QtCore, QtWidgets 12 | 13 | 14 | class Ui_MacroDialog(object): 15 | def setupUi(self, MacroDialog): 16 | MacroDialog.setObjectName("MacroDialog") 17 | MacroDialog.resize(528, 362) 18 | self.verticalLayout = QtWidgets.QVBoxLayout(MacroDialog) 19 | self.verticalLayout.setObjectName("verticalLayout") 20 | self.macroGroupBox = QtWidgets.QGroupBox(MacroDialog) 21 | sizePolicy = QtWidgets.QSizePolicy( 22 | QtWidgets.QSizePolicy.Policy.Preferred, 23 | QtWidgets.QSizePolicy.Policy.Expanding 24 | ) 25 | sizePolicy.setHorizontalStretch(0) 26 | sizePolicy.setVerticalStretch(0) 27 | sizePolicy.setHeightForWidth(self.macroGroupBox.sizePolicy().hasHeightForWidth()) 28 | self.macroGroupBox.setSizePolicy(sizePolicy) 29 | self.macroGroupBox.setObjectName("macroGroupBox") 30 | self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.macroGroupBox) 31 | self.verticalLayout_2.setContentsMargins(6, 0, 6, 6) 32 | self.verticalLayout_2.setObjectName("verticalLayout_2") 33 | self.filterLineEdit = QtWidgets.QLineEdit(self.macroGroupBox) 34 | self.filterLineEdit.setClearButtonEnabled(True) 35 | self.filterLineEdit.setObjectName("filterLineEdit") 36 | self.verticalLayout_2.addWidget(self.filterLineEdit) 37 | self.listView = QtWidgets.QListView(self.macroGroupBox) 38 | self.listView.setFrameShape(QtWidgets.QFrame.Shape.NoFrame) 39 | self.listView.setFrameShadow(QtWidgets.QFrame.Shadow.Plain) 40 | self.listView.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarPolicy.ScrollBarAlwaysOff) 41 | self.listView.setTabKeyNavigation(True) 42 | self.listView.setProperty("showDropIndicator", False) 43 | self.listView.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectionBehavior.SelectRows) 44 | self.listView.setObjectName("listView") 45 | self.verticalLayout_2.addWidget(self.listView) 46 | self.verticalLayout.addWidget(self.macroGroupBox) 47 | self.previewGroupBox = QtWidgets.QGroupBox(MacroDialog) 48 | self.previewGroupBox.setMinimumSize(QtCore.QSize(0, 42)) 49 | self.previewGroupBox.setObjectName("previewGroupBox") 50 | self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.previewGroupBox) 51 | self.verticalLayout_3.setContentsMargins(6, 6, 6, 6) 52 | self.verticalLayout_3.setObjectName("verticalLayout_3") 53 | self.previewLabel = QtWidgets.QLabel(self.previewGroupBox) 54 | self.previewLabel.setObjectName("previewLabel") 55 | self.verticalLayout_3.addWidget(self.previewLabel) 56 | self.verticalLayout.addWidget(self.previewGroupBox) 57 | self.buttonBox = QtWidgets.QDialogButtonBox(MacroDialog) 58 | self.buttonBox.setOrientation(QtCore.Qt.Orientation.Horizontal) 59 | self.buttonBox.setStandardButtons( 60 | QtWidgets.QDialogButtonBox.StandardButton.Cancel | QtWidgets.QDialogButtonBox.StandardButton.Ok 61 | ) 62 | self.buttonBox.setObjectName("buttonBox") 63 | self.verticalLayout.addWidget(self.buttonBox) 64 | 65 | self.retranslateUi(MacroDialog) 66 | self.buttonBox.accepted.connect(MacroDialog.accept) # type: ignore 67 | self.buttonBox.rejected.connect(MacroDialog.reject) # type: ignore 68 | QtCore.QMetaObject.connectSlotsByName(MacroDialog) 69 | 70 | def retranslateUi(self, MacroDialog): 71 | _translate = QtCore.QCoreApplication.translate 72 | MacroDialog.setWindowTitle(_translate("MacroDialog", "Macros")) 73 | self.macroGroupBox.setTitle(_translate("MacroDialog", "Macros")) 74 | self.filterLineEdit.setPlaceholderText(_translate("MacroDialog", "Filter...")) 75 | self.previewGroupBox.setTitle(_translate("MacroDialog", "Preview")) 76 | self.previewLabel.setText(_translate("MacroDialog", "test")) 77 | 78 | 79 | if __name__ == "__main__": 80 | import sys 81 | 82 | app = QtWidgets.QApplication(sys.argv) 83 | MacroDialog = QtWidgets.QDialog() 84 | ui = Ui_MacroDialog() 85 | ui.setupUi(MacroDialog) 86 | MacroDialog.show() 87 | sys.exit(app.exec_()) 88 | -------------------------------------------------------------------------------- /res/ui/Qt5/options_dialog.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file 'options_dialog.ui' 4 | # 5 | # Created by: PyQt5 UI code generator 5.15.7 6 | # 7 | # WARNING: Any manual changes made to this file will be lost when pyuic5 is 8 | # run again. Do not edit this file unless you know what you are doing. 9 | 10 | 11 | from PyQt5 import QtCore, QtGui, QtWidgets 12 | 13 | 14 | class Ui_OptionsDialog(object): 15 | def setupUi(self, OptionsDialog): 16 | OptionsDialog.setObjectName("OptionsDialog") 17 | OptionsDialog.setWindowModality(QtCore.Qt.WindowModal) 18 | OptionsDialog.resize(517, 620) 19 | OptionsDialog.setSizeGripEnabled(True) 20 | OptionsDialog.setModal(True) 21 | self.verticalLayout_4 = QtWidgets.QVBoxLayout(OptionsDialog) 22 | self.verticalLayout_4.setObjectName("verticalLayout_4") 23 | self.tabs_widget = QtWidgets.QTabWidget(OptionsDialog) 24 | self.tabs_widget.setEnabled(True) 25 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Expanding) 26 | sizePolicy.setHorizontalStretch(0) 27 | sizePolicy.setVerticalStretch(0) 28 | sizePolicy.setHeightForWidth(self.tabs_widget.sizePolicy().hasHeightForWidth()) 29 | self.tabs_widget.setSizePolicy(sizePolicy) 30 | self.tabs_widget.setTabPosition(QtWidgets.QTabWidget.North) 31 | self.tabs_widget.setTabShape(QtWidgets.QTabWidget.Rounded) 32 | self.tabs_widget.setObjectName("tabs_widget") 33 | self.appearance_tab = QtWidgets.QWidget() 34 | self.appearance_tab.setEnabled(True) 35 | self.appearance_tab.setObjectName("appearance_tab") 36 | self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.appearance_tab) 37 | self.verticalLayout_3.setObjectName("verticalLayout_3") 38 | self.frame = QtWidgets.QFrame(self.appearance_tab) 39 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) 40 | sizePolicy.setHorizontalStretch(0) 41 | sizePolicy.setVerticalStretch(0) 42 | sizePolicy.setHeightForWidth(self.frame.sizePolicy().hasHeightForWidth()) 43 | self.frame.setSizePolicy(sizePolicy) 44 | self.frame.setObjectName("frame") 45 | self.verticalLayout_8 = QtWidgets.QVBoxLayout(self.frame) 46 | self.verticalLayout_8.setObjectName("verticalLayout_8") 47 | self.pages_group = QtWidgets.QGroupBox(self.frame) 48 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum) 49 | sizePolicy.setHorizontalStretch(0) 50 | sizePolicy.setVerticalStretch(0) 51 | sizePolicy.setHeightForWidth(self.pages_group.sizePolicy().hasHeightForWidth()) 52 | self.pages_group.setSizePolicy(sizePolicy) 53 | self.pages_group.setObjectName("pages_group") 54 | self.horizontalLayout = QtWidgets.QHBoxLayout(self.pages_group) 55 | self.horizontalLayout.setObjectName("horizontalLayout") 56 | self.browser_checkbox = QtWidgets.QCheckBox(self.pages_group) 57 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed) 58 | sizePolicy.setHorizontalStretch(0) 59 | sizePolicy.setVerticalStretch(0) 60 | sizePolicy.setHeightForWidth(self.browser_checkbox.sizePolicy().hasHeightForWidth()) 61 | self.browser_checkbox.setSizePolicy(sizePolicy) 62 | self.browser_checkbox.setChecked(True) 63 | self.browser_checkbox.setObjectName("browser_checkbox") 64 | self.horizontalLayout.addWidget(self.browser_checkbox) 65 | self.overview_checkbox = QtWidgets.QCheckBox(self.pages_group) 66 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed) 67 | sizePolicy.setHorizontalStretch(0) 68 | sizePolicy.setVerticalStretch(0) 69 | sizePolicy.setHeightForWidth(self.overview_checkbox.sizePolicy().hasHeightForWidth()) 70 | self.overview_checkbox.setSizePolicy(sizePolicy) 71 | self.overview_checkbox.setChecked(True) 72 | self.overview_checkbox.setObjectName("overview_checkbox") 73 | self.horizontalLayout.addWidget(self.overview_checkbox) 74 | self.congrats_checkbox = QtWidgets.QCheckBox(self.pages_group) 75 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed) 76 | sizePolicy.setHorizontalStretch(0) 77 | sizePolicy.setVerticalStretch(0) 78 | sizePolicy.setHeightForWidth(self.congrats_checkbox.sizePolicy().hasHeightForWidth()) 79 | self.congrats_checkbox.setSizePolicy(sizePolicy) 80 | self.congrats_checkbox.setChecked(True) 81 | self.congrats_checkbox.setObjectName("congrats_checkbox") 82 | self.horizontalLayout.addWidget(self.congrats_checkbox) 83 | self.verticalLayout_8.addWidget(self.pages_group) 84 | self.mainViewGroupbox = QtWidgets.QGroupBox(self.frame) 85 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) 86 | sizePolicy.setHorizontalStretch(0) 87 | sizePolicy.setVerticalStretch(0) 88 | sizePolicy.setHeightForWidth(self.mainViewGroupbox.sizePolicy().hasHeightForWidth()) 89 | self.mainViewGroupbox.setSizePolicy(sizePolicy) 90 | self.mainViewGroupbox.setAlignment(QtCore.Qt.AlignLeading | QtCore.Qt.AlignLeft | QtCore.Qt.AlignTop) 91 | self.mainViewGroupbox.setObjectName("mainViewGroupbox") 92 | self.verticalLayout_5 = QtWidgets.QVBoxLayout(self.mainViewGroupbox) 93 | self.verticalLayout_5.setObjectName("verticalLayout_5") 94 | self.cellListWidget = QtWidgets.QListWidget(self.mainViewGroupbox) 95 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) 96 | sizePolicy.setHorizontalStretch(0) 97 | sizePolicy.setVerticalStretch(0) 98 | sizePolicy.setHeightForWidth(self.cellListWidget.sizePolicy().hasHeightForWidth()) 99 | self.cellListWidget.setSizePolicy(sizePolicy) 100 | self.cellListWidget.setMinimumSize(QtCore.QSize(0, 64)) 101 | self.cellListWidget.setFrameShape(QtWidgets.QFrame.NoFrame) 102 | self.cellListWidget.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) 103 | self.cellListWidget.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustIgnored) 104 | self.cellListWidget.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) 105 | self.cellListWidget.setProperty("showDropIndicator", False) 106 | self.cellListWidget.setDragEnabled(True) 107 | self.cellListWidget.setDragDropMode(QtWidgets.QAbstractItemView.InternalMove) 108 | self.cellListWidget.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection) 109 | self.cellListWidget.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel) 110 | self.cellListWidget.setObjectName("cellListWidget") 111 | self.verticalLayout_5.addWidget(self.cellListWidget) 112 | self.verticalLayout_8.addWidget(self.mainViewGroupbox) 113 | self.verticalLayout_3.addWidget(self.frame) 114 | self.tabs_widget.addTab(self.appearance_tab, "") 115 | self.decks_tab = QtWidgets.QWidget() 116 | self.decks_tab.setObjectName("decks_tab") 117 | self.verticalLayout = QtWidgets.QVBoxLayout(self.decks_tab) 118 | self.verticalLayout.setObjectName("verticalLayout") 119 | self.toolbar_checkbox = QtWidgets.QCheckBox(self.decks_tab) 120 | self.toolbar_checkbox.setChecked(True) 121 | self.toolbar_checkbox.setObjectName("toolbar_checkbox") 122 | self.verticalLayout.addWidget(self.toolbar_checkbox) 123 | self.include_deleted_checkbox = QtWidgets.QCheckBox(self.decks_tab) 124 | self.include_deleted_checkbox.setChecked(True) 125 | self.include_deleted_checkbox.setObjectName("include_deleted_checkbox") 126 | self.verticalLayout.addWidget(self.include_deleted_checkbox) 127 | self.useRolloverCheckbox = QtWidgets.QCheckBox(self.decks_tab) 128 | self.useRolloverCheckbox.setChecked(True) 129 | self.useRolloverCheckbox.setObjectName("useRolloverCheckbox") 130 | self.verticalLayout.addWidget(self.useRolloverCheckbox) 131 | self.useDecimalCheckbox = QtWidgets.QCheckBox(self.decks_tab) 132 | self.useDecimalCheckbox.setChecked(True) 133 | self.useDecimalCheckbox.setObjectName("useDecimalCheckbox") 134 | self.verticalLayout.addWidget(self.useDecimalCheckbox) 135 | self.enabled_decks_group = QtWidgets.QGroupBox(self.decks_tab) 136 | self.enabled_decks_group.setMinimumSize(QtCore.QSize(0, 100)) 137 | self.enabled_decks_group.setObjectName("enabled_decks_group") 138 | self.enabled_decks_layout = QtWidgets.QVBoxLayout(self.enabled_decks_group) 139 | self.enabled_decks_layout.setObjectName("enabled_decks_layout") 140 | self.exclude_layout = QtWidgets.QHBoxLayout() 141 | self.exclude_layout.setObjectName("exclude_layout") 142 | self.deck_enable_button = QtWidgets.QPushButton(self.enabled_decks_group) 143 | self.deck_enable_button.setFocusPolicy(QtCore.Qt.TabFocus) 144 | self.deck_enable_button.setObjectName("deck_enable_button") 145 | self.exclude_layout.addWidget(self.deck_enable_button) 146 | self.deck_disable_button = QtWidgets.QPushButton(self.enabled_decks_group) 147 | self.deck_disable_button.setFocusPolicy(QtCore.Qt.TabFocus) 148 | self.deck_disable_button.setObjectName("deck_disable_button") 149 | self.exclude_layout.addWidget(self.deck_disable_button) 150 | spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) 151 | self.exclude_layout.addItem(spacerItem) 152 | self.enabled_decks_layout.addLayout(self.exclude_layout) 153 | self.excluded_decks_list = QtWidgets.QListWidget(self.enabled_decks_group) 154 | self.excluded_decks_list.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection) 155 | self.excluded_decks_list.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows) 156 | self.excluded_decks_list.setObjectName("excluded_decks_list") 157 | self.enabled_decks_layout.addWidget(self.excluded_decks_list) 158 | self.verticalLayout.addWidget(self.enabled_decks_group) 159 | self.tabs_widget.addTab(self.decks_tab, "") 160 | self.about_tab = QtWidgets.QWidget() 161 | self.about_tab.setObjectName("about_tab") 162 | self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.about_tab) 163 | self.verticalLayout_2.setSizeConstraint(QtWidgets.QLayout.SetNoConstraint) 164 | self.verticalLayout_2.setObjectName("verticalLayout_2") 165 | self.scroll_area = QtWidgets.QScrollArea(self.about_tab) 166 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) 167 | sizePolicy.setHorizontalStretch(0) 168 | sizePolicy.setVerticalStretch(0) 169 | sizePolicy.setHeightForWidth(self.scroll_area.sizePolicy().hasHeightForWidth()) 170 | self.scroll_area.setSizePolicy(sizePolicy) 171 | self.scroll_area.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents) 172 | self.scroll_area.setWidgetResizable(True) 173 | self.scroll_area.setObjectName("scroll_area") 174 | self.about_scroll = QtWidgets.QWidget() 175 | self.about_scroll.setGeometry(QtCore.QRect(0, 0, 456, 555)) 176 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) 177 | sizePolicy.setHorizontalStretch(0) 178 | sizePolicy.setVerticalStretch(0) 179 | sizePolicy.setHeightForWidth(self.about_scroll.sizePolicy().hasHeightForWidth()) 180 | self.about_scroll.setSizePolicy(sizePolicy) 181 | self.about_scroll.setObjectName("about_scroll") 182 | self.scroll_layout = QtWidgets.QVBoxLayout(self.about_scroll) 183 | self.scroll_layout.setObjectName("scroll_layout") 184 | self.about_label_header = QtWidgets.QLabel(self.about_scroll) 185 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) 186 | sizePolicy.setHorizontalStretch(0) 187 | sizePolicy.setVerticalStretch(0) 188 | sizePolicy.setHeightForWidth(self.about_label_header.sizePolicy().hasHeightForWidth()) 189 | self.about_label_header.setSizePolicy(sizePolicy) 190 | self.about_label_header.setTextFormat(QtCore.Qt.AutoText) 191 | self.about_label_header.setAlignment(QtCore.Qt.AlignLeading | QtCore.Qt.AlignLeft | QtCore.Qt.AlignTop) 192 | self.about_label_header.setWordWrap(True) 193 | self.about_label_header.setOpenExternalLinks(True) 194 | self.about_label_header.setTextInteractionFlags( 195 | QtCore.Qt.LinksAccessibleByKeyboard | QtCore.Qt.LinksAccessibleByMouse | QtCore.Qt.TextBrowserInteraction 196 | | QtCore.Qt.TextSelectableByKeyboard | QtCore.Qt.TextSelectableByMouse 197 | ) 198 | self.about_label_header.setObjectName("about_label_header") 199 | self.scroll_layout.addWidget(self.about_label_header) 200 | self.supportButtonHolder = QtWidgets.QFrame(self.about_scroll) 201 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Maximum) 202 | sizePolicy.setHorizontalStretch(0) 203 | sizePolicy.setVerticalStretch(0) 204 | sizePolicy.setHeightForWidth(self.supportButtonHolder.sizePolicy().hasHeightForWidth()) 205 | self.supportButtonHolder.setSizePolicy(sizePolicy) 206 | self.supportButtonHolder.setObjectName("supportButtonHolder") 207 | self.support_buttons = QtWidgets.QHBoxLayout(self.supportButtonHolder) 208 | self.support_buttons.setContentsMargins(6, 6, 6, 6) 209 | self.support_buttons.setObjectName("support_buttons") 210 | self.like_button = QtWidgets.QPushButton(self.supportButtonHolder) 211 | self.like_button.setMinimumSize(QtCore.QSize(0, 42)) 212 | self.like_button.setMaximumSize(QtCore.QSize(200, 16777215)) 213 | self.like_button.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor)) 214 | self.like_button.setFocusPolicy(QtCore.Qt.NoFocus) 215 | self.like_button.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) 216 | icon = QtGui.QIcon() 217 | icon.addPixmap(QtGui.QPixmap("res/img/anki_like.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) 218 | self.like_button.setIcon(icon) 219 | self.like_button.setIconSize(QtCore.QSize(32, 32)) 220 | self.like_button.setObjectName("like_button") 221 | self.support_buttons.addWidget(self.like_button) 222 | self.patreon_button = QtWidgets.QPushButton(self.supportButtonHolder) 223 | self.patreon_button.setMinimumSize(QtCore.QSize(0, 42)) 224 | self.patreon_button.setMaximumSize(QtCore.QSize(200, 16777215)) 225 | self.patreon_button.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor)) 226 | self.patreon_button.setFocusPolicy(QtCore.Qt.NoFocus) 227 | self.patreon_button.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) 228 | icon1 = QtGui.QIcon() 229 | icon1.addPixmap(QtGui.QPixmap("res/img/patreon.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) 230 | self.patreon_button.setIcon(icon1) 231 | self.patreon_button.setIconSize(QtCore.QSize(32, 32)) 232 | self.patreon_button.setObjectName("patreon_button") 233 | self.support_buttons.addWidget(self.patreon_button) 234 | self.kofi_button = QtWidgets.QPushButton(self.supportButtonHolder) 235 | self.kofi_button.setMinimumSize(QtCore.QSize(0, 42)) 236 | self.kofi_button.setMaximumSize(QtCore.QSize(200, 16777215)) 237 | self.kofi_button.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor)) 238 | self.kofi_button.setFocusPolicy(QtCore.Qt.NoFocus) 239 | self.kofi_button.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) 240 | icon2 = QtGui.QIcon() 241 | icon2.addPixmap(QtGui.QPixmap("res/img/kofilogo_blue.PNG"), QtGui.QIcon.Normal, QtGui.QIcon.Off) 242 | self.kofi_button.setIcon(icon2) 243 | self.kofi_button.setIconSize(QtCore.QSize(32, 32)) 244 | self.kofi_button.setObjectName("kofi_button") 245 | self.support_buttons.addWidget(self.kofi_button) 246 | self.scroll_layout.addWidget(self.supportButtonHolder) 247 | self.about_label_body = QtWidgets.QLabel(self.about_scroll) 248 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Expanding) 249 | sizePolicy.setHorizontalStretch(0) 250 | sizePolicy.setVerticalStretch(0) 251 | sizePolicy.setHeightForWidth(self.about_label_body.sizePolicy().hasHeightForWidth()) 252 | self.about_label_body.setSizePolicy(sizePolicy) 253 | self.about_label_body.setTextFormat(QtCore.Qt.AutoText) 254 | self.about_label_body.setAlignment(QtCore.Qt.AlignLeading | QtCore.Qt.AlignLeft | QtCore.Qt.AlignTop) 255 | self.about_label_body.setWordWrap(True) 256 | self.about_label_body.setOpenExternalLinks(True) 257 | self.about_label_body.setTextInteractionFlags( 258 | QtCore.Qt.LinksAccessibleByKeyboard | QtCore.Qt.LinksAccessibleByMouse | QtCore.Qt.TextBrowserInteraction 259 | | QtCore.Qt.TextSelectableByKeyboard | QtCore.Qt.TextSelectableByMouse 260 | ) 261 | self.about_label_body.setObjectName("about_label_body") 262 | self.scroll_layout.addWidget(self.about_label_body) 263 | self.scroll_area.setWidget(self.about_scroll) 264 | self.verticalLayout_2.addWidget(self.scroll_area) 265 | self.tabs_widget.addTab(self.about_tab, "") 266 | self.verticalLayout_4.addWidget(self.tabs_widget) 267 | self.frame_2 = QtWidgets.QFrame(OptionsDialog) 268 | self.frame_2.setObjectName("frame_2") 269 | self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.frame_2) 270 | self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0) 271 | self.horizontalLayout_2.setObjectName("horizontalLayout_2") 272 | self.confirm_button_box = QtWidgets.QDialogButtonBox(self.frame_2) 273 | self.confirm_button_box.setOrientation(QtCore.Qt.Horizontal) 274 | self.confirm_button_box.setStandardButtons( 275 | QtWidgets.QDialogButtonBox.Apply | QtWidgets.QDialogButtonBox.Cancel | QtWidgets.QDialogButtonBox.Ok | 276 | QtWidgets.QDialogButtonBox.RestoreDefaults 277 | ) 278 | self.confirm_button_box.setObjectName("confirm_button_box") 279 | self.horizontalLayout_2.addWidget(self.confirm_button_box) 280 | self.supportButton = HoverButton(self.frame_2) 281 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) 282 | sizePolicy.setHorizontalStretch(0) 283 | sizePolicy.setVerticalStretch(0) 284 | sizePolicy.setHeightForWidth(self.supportButton.sizePolicy().hasHeightForWidth()) 285 | self.supportButton.setSizePolicy(sizePolicy) 286 | self.supportButton.setObjectName("supportButton") 287 | self.horizontalLayout_2.addWidget(self.supportButton) 288 | self.verticalLayout_4.addWidget(self.frame_2) 289 | 290 | self.retranslateUi(OptionsDialog) 291 | self.tabs_widget.setCurrentIndex(0) 292 | self.confirm_button_box.accepted.connect(OptionsDialog.accept) # type: ignore 293 | self.confirm_button_box.rejected.connect(OptionsDialog.reject) # type: ignore 294 | QtCore.QMetaObject.connectSlotsByName(OptionsDialog) 295 | 296 | def retranslateUi(self, OptionsDialog): 297 | _translate = QtCore.QCoreApplication.translate 298 | OptionsDialog.setWindowTitle(_translate("OptionsDialog", "Study Time Stats Options")) 299 | self.pages_group.setTitle(_translate("OptionsDialog", "Enabled Pages")) 300 | self.browser_checkbox.setToolTip(_translate("OptionsDialog", "The main page for browsing decks.")) 301 | self.browser_checkbox.setText(_translate("OptionsDialog", "Deck Browser")) 302 | self.overview_checkbox.setToolTip(_translate("OptionsDialog", "The page that shows when viewing a deck.")) 303 | self.overview_checkbox.setText(_translate("OptionsDialog", "Overview")) 304 | self.congrats_checkbox.setToolTip( 305 | _translate( 306 | "OptionsDialog", 307 | "The page that shows when viewing a deck that has its reviews done for the day." 308 | ) 309 | ) 310 | self.congrats_checkbox.setText(_translate("OptionsDialog", "Congrats")) 311 | self.mainViewGroupbox.setTitle(_translate("OptionsDialog", "Stats View")) 312 | self.cellListWidget.setSortingEnabled(True) 313 | self.tabs_widget.setTabText( 314 | self.tabs_widget.indexOf(self.appearance_tab), 315 | _translate("OptionsDialog", "General") 316 | ) 317 | self.toolbar_checkbox.setToolTip( 318 | _translate( 319 | "OptionsDialog", "Enable the Tools Menu shortcut for these options. \n" 320 | "(Can also be accessed via Tools>Add-ons>Study Time Stats>Config)" 321 | ) 322 | ) 323 | self.toolbar_checkbox.setText(_translate("OptionsDialog", "Show options shortcut in the Tools Menu")) 324 | self.include_deleted_checkbox.setToolTip( 325 | _translate("OptionsDialog", "Include review/time stats for cards that\'ve been deleted.") 326 | ) 327 | self.include_deleted_checkbox.setText(_translate("OptionsDialog", "Include reviews from deleted cards")) 328 | self.useRolloverCheckbox.setToolTip( 329 | _translate( 330 | "OptionsDialog", 331 | "Use Anki\'s rollover (next-day) hour when considering the time days are cut off." 332 | ) 333 | ) 334 | self.useRolloverCheckbox.setText( 335 | _translate("OptionsDialog", "Use the next-day hour when calculating ranged times") 336 | ) 337 | self.useDecimalCheckbox.setToolTip( 338 | _translate( 339 | "OptionsDialog", "Shows an \"hour.min\" format for all time outputs. \n" 340 | "(Otherwise uses \"hh:mm\")" 341 | ) 342 | ) 343 | self.useDecimalCheckbox.setText(_translate("OptionsDialog", "Use decimal format time outputs")) 344 | self.enabled_decks_group.setTitle(_translate("OptionsDialog", "Enabled Decks")) 345 | self.deck_enable_button.setToolTip(_translate("OptionsDialog", "Enable stats for the selected deck(s).")) 346 | self.deck_enable_button.setText(_translate("OptionsDialog", "Enable")) 347 | self.deck_disable_button.setToolTip(_translate("OptionsDialog", "Disable stats for the selected deck(s).")) 348 | self.deck_disable_button.setText(_translate("OptionsDialog", "Disable")) 349 | self.tabs_widget.setTabText(self.tabs_widget.indexOf(self.decks_tab), _translate("OptionsDialog", "Advanced")) 350 | self.about_label_header.setText( 351 | _translate( 352 | "OptionsDialog", "##

Study Time Stats

\n" 353 | "Adds a total and ranged study time statistic to Anki\'s main window. \n" 354 | "\n" 355 | "Version: {version} \n" 356 | "Have any issues or feedback? Feel free to post on the project\'s issue section on [" 357 | "GitHub](https://github.com/iamjustkoi/StudyTimeStats/issues)! \n" 358 | "\n" 359 | "[Releases/Changelog](https://github.com/iamjustkoi/StudyTimeStats/releases) \n" 360 | "[Source Code](https://github.com/iamjustkoi/StudyTimeStats) \n" 361 | "\n" 362 | "If you like the add-on and want to consider supporting me in anyway:" 363 | ) 364 | ) 365 | self.like_button.setToolTip(_translate("OptionsDialog", "Leave a review over at AnkiWeb!")) 366 | self.like_button.setText(_translate("OptionsDialog", "AnkiWeb ")) 367 | self.patreon_button.setToolTip(_translate("OptionsDialog", "Follow/support me on Patreon!")) 368 | self.patreon_button.setText(_translate("OptionsDialog", " Patreon ")) 369 | self.kofi_button.setToolTip(_translate("OptionsDialog", "Buy me a coffee with Ko-Fi!")) 370 | self.kofi_button.setText(_translate("OptionsDialog", "Ko-Fi")) 371 | self.about_label_body.setText( 372 | _translate( 373 | "OptionsDialog", "Every bit helps and is greatly appreciated! <3\n" 374 | "\n" 375 | "### Text Macros\n" 376 | "All output text can also be filtered to show some more customized information (e.g. " 377 | "\"Past %range\" to \"Past Week\"). These can be used multiple times and will update " 378 | "whenever Anki\'s main window reloads. \n" 379 | "\n" 380 | "`%%` - can be used to return a single % symbol and disable filtering for any macro " 381 | "text (e.g. `%%` -> %, `%%range` -> %range)\n" 382 | "\n" 383 | "*Small warning: as a general rule, the more stats used/the larger the range of the " 384 | "stat, the longer it might take to load them all (some caching is also done on the " 385 | "side, too though).\n" 386 | "\n" 387 | "

\n" 388 | "

\n" 389 | "Thanks for downloading and hope you enjoy!\n" 390 | "\n" 391 | "-koi \n" 392 | "\n" 393 | "\n" 394 | "

\n" 395 | "

\n" 396 | "MIT License \n" 397 | "\n" 398 | "©2022-2023 JustKoi (iamjustkoi)" 399 | ) 400 | ) 401 | self.tabs_widget.setTabText(self.tabs_widget.indexOf(self.about_tab), _translate("OptionsDialog", "About")) 402 | self.supportButton.setText(_translate("OptionsDialog", "<3")) 403 | 404 | 405 | from ..forms import HoverButton 406 | 407 | if __name__ == "__main__": 408 | import sys 409 | 410 | app = QtWidgets.QApplication(sys.argv) 411 | OptionsDialog = QtWidgets.QDialog() 412 | ui = Ui_OptionsDialog() 413 | ui.setupUi(OptionsDialog) 414 | OptionsDialog.show() 415 | sys.exit(app.exec_()) 416 | -------------------------------------------------------------------------------- /res/ui/Qt6/cell_item.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file 'cell_item.ui' 4 | # 5 | # Created by: PyQt6 UI code generator 5.15.7 6 | # 7 | # WARNING: Any manual changes made to this file will be lost when pyuic5 is 8 | # run again. Do not edit this file unless you know what you are doing. 9 | 10 | 11 | from PyQt6 import QtCore, QtGui, QtWidgets 12 | 13 | 14 | class Ui_CellWidget(object): 15 | def setupUi(self, CellWidget): 16 | CellWidget.setObjectName("CellWidget") 17 | CellWidget.resize(460, 329) 18 | sizePolicy = QtWidgets.QSizePolicy( 19 | QtWidgets.QSizePolicy.Policy.Preferred, 20 | QtWidgets.QSizePolicy.Policy.Maximum 21 | ) 22 | sizePolicy.setHorizontalStretch(0) 23 | sizePolicy.setVerticalStretch(0) 24 | sizePolicy.setHeightForWidth(CellWidget.sizePolicy().hasHeightForWidth()) 25 | CellWidget.setSizePolicy(sizePolicy) 26 | self.verticalLayout = QtWidgets.QVBoxLayout(CellWidget) 27 | self.verticalLayout.setContentsMargins(6, 0, 6, 6) 28 | self.verticalLayout.setSpacing(0) 29 | self.verticalLayout.setObjectName("verticalLayout") 30 | self.addButton = HoverButton(CellWidget) 31 | sizePolicy = QtWidgets.QSizePolicy( 32 | QtWidgets.QSizePolicy.Policy.Preferred, 33 | QtWidgets.QSizePolicy.Policy.Fixed 34 | ) 35 | sizePolicy.setHorizontalStretch(0) 36 | sizePolicy.setVerticalStretch(0) 37 | sizePolicy.setHeightForWidth(self.addButton.sizePolicy().hasHeightForWidth()) 38 | self.addButton.setSizePolicy(sizePolicy) 39 | self.addButton.setMinimumSize(QtCore.QSize(0, 64)) 40 | self.addButton.setStyleSheet( 41 | "#addButton {\n" 42 | " border: 1px solid rgba(134, 134, 134, 128);\n" 43 | " border-radius: 2px;\n" 44 | " background: transparent;\n" 45 | "}" 46 | ) 47 | icon = QtGui.QIcon() 48 | icon.addPixmap(QtGui.QPixmap("../../img/add_icon.svg"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off) 49 | self.addButton.setIcon(icon) 50 | self.addButton.setIconSize(QtCore.QSize(20, 20)) 51 | self.addButton.setObjectName("addButton") 52 | self.verticalLayout.addWidget(self.addButton) 53 | self.mainFrame = QtWidgets.QFrame(CellWidget) 54 | sizePolicy = QtWidgets.QSizePolicy( 55 | QtWidgets.QSizePolicy.Policy.Ignored, 56 | QtWidgets.QSizePolicy.Policy.Maximum 57 | ) 58 | sizePolicy.setHorizontalStretch(0) 59 | sizePolicy.setVerticalStretch(0) 60 | sizePolicy.setHeightForWidth(self.mainFrame.sizePolicy().hasHeightForWidth()) 61 | self.mainFrame.setSizePolicy(sizePolicy) 62 | self.mainFrame.setStyleSheet( 63 | "#mainFrame {\n" 64 | " background: rgba(163, 163, 163, 5%);\n" 65 | " border-radius: 6px;\n" 66 | "}" 67 | ) 68 | self.mainFrame.setObjectName("mainFrame") 69 | self.gridLayout = QtWidgets.QGridLayout(self.mainFrame) 70 | self.gridLayout.setObjectName("gridLayout") 71 | self.titleColorButton = QtWidgets.QToolButton(self.mainFrame) 72 | sizePolicy = QtWidgets.QSizePolicy( 73 | QtWidgets.QSizePolicy.Policy.Fixed, 74 | QtWidgets.QSizePolicy.Policy.Fixed 75 | ) 76 | sizePolicy.setHorizontalStretch(0) 77 | sizePolicy.setVerticalStretch(0) 78 | sizePolicy.setHeightForWidth(self.titleColorButton.sizePolicy().hasHeightForWidth()) 79 | self.titleColorButton.setSizePolicy(sizePolicy) 80 | self.titleColorButton.setMaximumSize(QtCore.QSize(20, 20)) 81 | self.titleColorButton.setStyleSheet( 82 | "#titleColorButton {\n" 83 | " border-radius: 10px;\n" 84 | " background-color: #76bfb4;\n" 85 | " width: 20px;\n" 86 | "}" 87 | ) 88 | self.titleColorButton.setObjectName("titleColorButton") 89 | self.gridLayout.addWidget(self.titleColorButton, 0, 2, 1, 1) 90 | self.titleLineEdit = QtWidgets.QLineEdit(self.mainFrame) 91 | self.titleLineEdit.setObjectName("titleLineEdit") 92 | self.gridLayout.addWidget(self.titleLineEdit, 0, 1, 1, 1) 93 | self.titleLabel = QtWidgets.QLabel(self.mainFrame) 94 | sizePolicy = QtWidgets.QSizePolicy( 95 | QtWidgets.QSizePolicy.Policy.Fixed, 96 | QtWidgets.QSizePolicy.Policy.Maximum 97 | ) 98 | sizePolicy.setHorizontalStretch(0) 99 | sizePolicy.setVerticalStretch(0) 100 | sizePolicy.setHeightForWidth(self.titleLabel.sizePolicy().hasHeightForWidth()) 101 | self.titleLabel.setSizePolicy(sizePolicy) 102 | self.titleLabel.setMinimumSize(QtCore.QSize(82, 0)) 103 | self.titleLabel.setStyleSheet( 104 | "#mainFrame {\n" 105 | " background: rgba(163, 163, 163, 5%);\n" 106 | " border-radius: 6px;\n" 107 | "}" 108 | ) 109 | self.titleLabel.setObjectName("titleLabel") 110 | self.gridLayout.addWidget(self.titleLabel, 0, 0, 1, 1) 111 | self.dragHandle = DragHandle(self.mainFrame) 112 | sizePolicy = QtWidgets.QSizePolicy( 113 | QtWidgets.QSizePolicy.Policy.Fixed, 114 | QtWidgets.QSizePolicy.Policy.Fixed 115 | ) 116 | sizePolicy.setHorizontalStretch(0) 117 | sizePolicy.setVerticalStretch(0) 118 | sizePolicy.setHeightForWidth(self.dragHandle.sizePolicy().hasHeightForWidth()) 119 | self.dragHandle.setSizePolicy(sizePolicy) 120 | self.dragHandle.setMaximumSize(QtCore.QSize(20, 20)) 121 | self.dragHandle.setCursor(QtGui.QCursor(QtCore.Qt.CursorShape.OpenHandCursor)) 122 | self.dragHandle.setMouseTracking(True) 123 | icon1 = QtGui.QIcon() 124 | icon1.addPixmap( 125 | QtGui.QPixmap("../../img/vert_grip.svg"), 126 | QtGui.QIcon.Mode.Normal, 127 | QtGui.QIcon.State.Off 128 | ) 129 | self.dragHandle.setIcon(icon1) 130 | self.dragHandle.setObjectName("dragHandle") 131 | self.gridLayout.addWidget(self.dragHandle, 0, 4, 1, 1) 132 | self.expandoButton = RotateButton(self.mainFrame) 133 | sizePolicy = QtWidgets.QSizePolicy( 134 | QtWidgets.QSizePolicy.Policy.Fixed, 135 | QtWidgets.QSizePolicy.Policy.Fixed 136 | ) 137 | sizePolicy.setHorizontalStretch(0) 138 | sizePolicy.setVerticalStretch(0) 139 | sizePolicy.setHeightForWidth(self.expandoButton.sizePolicy().hasHeightForWidth()) 140 | self.expandoButton.setSizePolicy(sizePolicy) 141 | self.expandoButton.setMaximumSize(QtCore.QSize(20, 20)) 142 | icon2 = QtGui.QIcon() 143 | icon2.addPixmap( 144 | QtGui.QPixmap("../../img/chevron_down.svg"), 145 | QtGui.QIcon.Mode.Normal, 146 | QtGui.QIcon.State.Off 147 | ) 148 | self.expandoButton.setIcon(icon2) 149 | self.expandoButton.setIconSize(QtCore.QSize(20, 20)) 150 | self.expandoButton.setObjectName("expandoButton") 151 | self.gridLayout.addWidget(self.expandoButton, 0, 3, 1, 1) 152 | self.expandFrame = QtWidgets.QFrame(self.mainFrame) 153 | sizePolicy = QtWidgets.QSizePolicy( 154 | QtWidgets.QSizePolicy.Policy.Ignored, 155 | QtWidgets.QSizePolicy.Policy.Maximum 156 | ) 157 | sizePolicy.setHorizontalStretch(0) 158 | sizePolicy.setVerticalStretch(0) 159 | sizePolicy.setHeightForWidth(self.expandFrame.sizePolicy().hasHeightForWidth()) 160 | self.expandFrame.setSizePolicy(sizePolicy) 161 | self.expandFrame.setObjectName("expandFrame") 162 | self.gridLayout_2 = QtWidgets.QGridLayout(self.expandFrame) 163 | self.gridLayout_2.setContentsMargins(0, 0, 0, 0) 164 | self.gridLayout_2.setObjectName("gridLayout_2") 165 | self.startDayDropdown = QtWidgets.QComboBox(self.expandFrame) 166 | sizePolicy = QtWidgets.QSizePolicy( 167 | QtWidgets.QSizePolicy.Policy.Expanding, 168 | QtWidgets.QSizePolicy.Policy.Fixed 169 | ) 170 | sizePolicy.setHorizontalStretch(0) 171 | sizePolicy.setVerticalStretch(0) 172 | sizePolicy.setHeightForWidth(self.startDayDropdown.sizePolicy().hasHeightForWidth()) 173 | self.startDayDropdown.setSizePolicy(sizePolicy) 174 | self.startDayDropdown.setFocusPolicy(QtCore.Qt.FocusPolicy.StrongFocus) 175 | self.startDayDropdown.setSizeAdjustPolicy(QtWidgets.QComboBox.SizeAdjustPolicy.AdjustToContents) 176 | self.startDayDropdown.setObjectName("startDayDropdown") 177 | self.startDayDropdown.addItem("") 178 | self.startDayDropdown.addItem("") 179 | self.startDayDropdown.addItem("") 180 | self.startDayDropdown.addItem("") 181 | self.startDayDropdown.addItem("") 182 | self.startDayDropdown.addItem("") 183 | self.startDayDropdown.addItem("") 184 | self.gridLayout_2.addWidget(self.startDayDropdown, 3, 1, 1, 1) 185 | self.outputLineEdit = QtWidgets.QLineEdit(self.expandFrame) 186 | self.outputLineEdit.setObjectName("outputLineEdit") 187 | self.gridLayout_2.addWidget(self.outputLineEdit, 0, 1, 1, 1) 188 | self.outputColorButton = QtWidgets.QToolButton(self.expandFrame) 189 | sizePolicy = QtWidgets.QSizePolicy( 190 | QtWidgets.QSizePolicy.Policy.Fixed, 191 | QtWidgets.QSizePolicy.Policy.Fixed 192 | ) 193 | sizePolicy.setHorizontalStretch(0) 194 | sizePolicy.setVerticalStretch(0) 195 | sizePolicy.setHeightForWidth(self.outputColorButton.sizePolicy().hasHeightForWidth()) 196 | self.outputColorButton.setSizePolicy(sizePolicy) 197 | self.outputColorButton.setMaximumSize(QtCore.QSize(20, 20)) 198 | self.outputColorButton.setStyleSheet( 199 | "#outputColorButton {\n" 200 | " border-radius: 10px;\n" 201 | " background-color: #FFF;\n" 202 | " width: 20px;\n" 203 | "}" 204 | ) 205 | self.outputColorButton.setObjectName("outputColorButton") 206 | self.gridLayout_2.addWidget(self.outputColorButton, 0, 2, 1, 1) 207 | self.frame = QtWidgets.QFrame(self.expandFrame) 208 | sizePolicy = QtWidgets.QSizePolicy( 209 | QtWidgets.QSizePolicy.Policy.Expanding, 210 | QtWidgets.QSizePolicy.Policy.Preferred 211 | ) 212 | sizePolicy.setHorizontalStretch(0) 213 | sizePolicy.setVerticalStretch(0) 214 | sizePolicy.setHeightForWidth(self.frame.sizePolicy().hasHeightForWidth()) 215 | self.frame.setSizePolicy(sizePolicy) 216 | self.frame.setFrameShape(QtWidgets.QFrame.Shape.NoFrame) 217 | self.frame.setFrameShadow(QtWidgets.QFrame.Shadow.Raised) 218 | self.frame.setObjectName("frame") 219 | self.horizontalLayout = QtWidgets.QHBoxLayout(self.frame) 220 | self.horizontalLayout.setContentsMargins(0, 0, 0, 0) 221 | self.horizontalLayout.setObjectName("horizontalLayout") 222 | self.rangeDropdown = QtWidgets.QComboBox(self.frame) 223 | sizePolicy = QtWidgets.QSizePolicy( 224 | QtWidgets.QSizePolicy.Policy.Expanding, 225 | QtWidgets.QSizePolicy.Policy.Fixed 226 | ) 227 | sizePolicy.setHorizontalStretch(0) 228 | sizePolicy.setVerticalStretch(0) 229 | sizePolicy.setHeightForWidth(self.rangeDropdown.sizePolicy().hasHeightForWidth()) 230 | self.rangeDropdown.setSizePolicy(sizePolicy) 231 | self.rangeDropdown.setFocusPolicy(QtCore.Qt.FocusPolicy.StrongFocus) 232 | self.rangeDropdown.setSizeAdjustPolicy(QtWidgets.QComboBox.SizeAdjustPolicy.AdjustToContents) 233 | self.rangeDropdown.setObjectName("rangeDropdown") 234 | self.rangeDropdown.addItem("") 235 | self.rangeDropdown.addItem("") 236 | self.rangeDropdown.addItem("") 237 | self.rangeDropdown.addItem("") 238 | self.rangeDropdown.addItem("") 239 | self.rangeDropdown.addItem("") 240 | self.horizontalLayout.addWidget(self.rangeDropdown) 241 | self.rangeExtraFrame = QtWidgets.QFrame(self.frame) 242 | sizePolicy = QtWidgets.QSizePolicy( 243 | QtWidgets.QSizePolicy.Policy.Expanding, 244 | QtWidgets.QSizePolicy.Policy.Maximum 245 | ) 246 | sizePolicy.setHorizontalStretch(0) 247 | sizePolicy.setVerticalStretch(0) 248 | sizePolicy.setHeightForWidth(self.rangeExtraFrame.sizePolicy().hasHeightForWidth()) 249 | self.rangeExtraFrame.setSizePolicy(sizePolicy) 250 | self.rangeExtraFrame.setFrameShape(QtWidgets.QFrame.Shape.NoFrame) 251 | self.rangeExtraFrame.setFrameShadow(QtWidgets.QFrame.Shadow.Raised) 252 | self.rangeExtraFrame.setObjectName("rangeExtraFrame") 253 | self.horizontalLayout_4 = QtWidgets.QHBoxLayout(self.rangeExtraFrame) 254 | self.horizontalLayout_4.setContentsMargins(0, 0, 0, 0) 255 | self.horizontalLayout_4.setSpacing(0) 256 | self.horizontalLayout_4.setObjectName("horizontalLayout_4") 257 | self.calendarCheckbox = QtWidgets.QCheckBox(self.rangeExtraFrame) 258 | sizePolicy = QtWidgets.QSizePolicy( 259 | QtWidgets.QSizePolicy.Policy.Maximum, 260 | QtWidgets.QSizePolicy.Policy.Fixed 261 | ) 262 | sizePolicy.setHorizontalStretch(0) 263 | sizePolicy.setVerticalStretch(0) 264 | sizePolicy.setHeightForWidth(self.calendarCheckbox.sizePolicy().hasHeightForWidth()) 265 | self.calendarCheckbox.setSizePolicy(sizePolicy) 266 | self.calendarCheckbox.setChecked(True) 267 | self.calendarCheckbox.setObjectName("calendarCheckbox") 268 | self.horizontalLayout_4.addWidget(self.calendarCheckbox) 269 | self.customRangeSpinbox = QtWidgets.QSpinBox(self.rangeExtraFrame) 270 | sizePolicy = QtWidgets.QSizePolicy( 271 | QtWidgets.QSizePolicy.Policy.Maximum, 272 | QtWidgets.QSizePolicy.Policy.Fixed 273 | ) 274 | sizePolicy.setHorizontalStretch(0) 275 | sizePolicy.setVerticalStretch(0) 276 | sizePolicy.setHeightForWidth(self.customRangeSpinbox.sizePolicy().hasHeightForWidth()) 277 | self.customRangeSpinbox.setSizePolicy(sizePolicy) 278 | self.customRangeSpinbox.setFocusPolicy(QtCore.Qt.FocusPolicy.StrongFocus) 279 | self.customRangeSpinbox.setMaximum(5690) 280 | self.customRangeSpinbox.setProperty("value", 7) 281 | self.customRangeSpinbox.setObjectName("customRangeSpinbox") 282 | self.horizontalLayout_4.addWidget(self.customRangeSpinbox) 283 | self.horizontalLayout.addWidget(self.rangeExtraFrame) 284 | self.gridLayout_2.addWidget(self.frame, 2, 1, 1, 1) 285 | self.directionFrame = QtWidgets.QFrame(self.expandFrame) 286 | sizePolicy = QtWidgets.QSizePolicy( 287 | QtWidgets.QSizePolicy.Policy.Maximum, 288 | QtWidgets.QSizePolicy.Policy.Maximum 289 | ) 290 | sizePolicy.setHorizontalStretch(0) 291 | sizePolicy.setVerticalStretch(0) 292 | sizePolicy.setHeightForWidth(self.directionFrame.sizePolicy().hasHeightForWidth()) 293 | self.directionFrame.setSizePolicy(sizePolicy) 294 | self.directionFrame.setFrameShape(QtWidgets.QFrame.Shape.NoFrame) 295 | self.directionFrame.setFrameShadow(QtWidgets.QFrame.Shadow.Plain) 296 | self.directionFrame.setObjectName("directionFrame") 297 | self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.directionFrame) 298 | self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0) 299 | self.horizontalLayout_2.setSpacing(0) 300 | self.horizontalLayout_2.setObjectName("horizontalLayout_2") 301 | self.directionVerticalButton = QtWidgets.QPushButton(self.directionFrame) 302 | self.directionVerticalButton.setEnabled(False) 303 | sizePolicy = QtWidgets.QSizePolicy( 304 | QtWidgets.QSizePolicy.Policy.Expanding, 305 | QtWidgets.QSizePolicy.Policy.Fixed 306 | ) 307 | sizePolicy.setHorizontalStretch(0) 308 | sizePolicy.setVerticalStretch(0) 309 | sizePolicy.setHeightForWidth(self.directionVerticalButton.sizePolicy().hasHeightForWidth()) 310 | self.directionVerticalButton.setSizePolicy(sizePolicy) 311 | self.directionVerticalButton.setMaximumSize(QtCore.QSize(42, 16777215)) 312 | icon3 = QtGui.QIcon() 313 | icon3.addPixmap( 314 | QtGui.QPixmap("../../img/vert_lines.svg"), 315 | QtGui.QIcon.Mode.Normal, 316 | QtGui.QIcon.State.Off 317 | ) 318 | self.directionVerticalButton.setIcon(icon3) 319 | self.directionVerticalButton.setObjectName("directionVerticalButton") 320 | self.horizontalLayout_2.addWidget(self.directionVerticalButton) 321 | self.directionHorizontalButton = QtWidgets.QPushButton(self.directionFrame) 322 | sizePolicy = QtWidgets.QSizePolicy( 323 | QtWidgets.QSizePolicy.Policy.Expanding, 324 | QtWidgets.QSizePolicy.Policy.Fixed 325 | ) 326 | sizePolicy.setHorizontalStretch(0) 327 | sizePolicy.setVerticalStretch(0) 328 | sizePolicy.setHeightForWidth(self.directionHorizontalButton.sizePolicy().hasHeightForWidth()) 329 | self.directionHorizontalButton.setSizePolicy(sizePolicy) 330 | self.directionHorizontalButton.setMaximumSize(QtCore.QSize(42, 16777215)) 331 | icon4 = QtGui.QIcon() 332 | icon4.addPixmap( 333 | QtGui.QPixmap("../../img/horiz_lines.svg"), 334 | QtGui.QIcon.Mode.Normal, 335 | QtGui.QIcon.State.Off 336 | ) 337 | self.directionHorizontalButton.setIcon(icon4) 338 | self.directionHorizontalButton.setObjectName("directionHorizontalButton") 339 | self.horizontalLayout_2.addWidget(self.directionHorizontalButton) 340 | self.gridLayout_2.addWidget(self.directionFrame, 4, 1, 1, 1) 341 | self.frame_3 = QtWidgets.QFrame(self.expandFrame) 342 | sizePolicy = QtWidgets.QSizePolicy( 343 | QtWidgets.QSizePolicy.Policy.Expanding, 344 | QtWidgets.QSizePolicy.Policy.Preferred 345 | ) 346 | sizePolicy.setHorizontalStretch(0) 347 | sizePolicy.setVerticalStretch(0) 348 | sizePolicy.setHeightForWidth(self.frame_3.sizePolicy().hasHeightForWidth()) 349 | self.frame_3.setSizePolicy(sizePolicy) 350 | self.frame_3.setFrameShape(QtWidgets.QFrame.Shape.NoFrame) 351 | self.frame_3.setFrameShadow(QtWidgets.QFrame.Shadow.Raised) 352 | self.frame_3.setObjectName("frame_3") 353 | self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.frame_3) 354 | self.horizontalLayout_3.setContentsMargins(0, 0, 0, 0) 355 | self.horizontalLayout_3.setObjectName("horizontalLayout_3") 356 | self.hourLabel = QtWidgets.QLabel(self.frame_3) 357 | self.hourLabel.setObjectName("hourLabel") 358 | self.horizontalLayout_3.addWidget(self.hourLabel) 359 | self.hourEdit = QtWidgets.QLineEdit(self.frame_3) 360 | sizePolicy = QtWidgets.QSizePolicy( 361 | QtWidgets.QSizePolicy.Policy.Expanding, 362 | QtWidgets.QSizePolicy.Policy.Fixed 363 | ) 364 | sizePolicy.setHorizontalStretch(0) 365 | sizePolicy.setVerticalStretch(0) 366 | sizePolicy.setHeightForWidth(self.hourEdit.sizePolicy().hasHeightForWidth()) 367 | self.hourEdit.setSizePolicy(sizePolicy) 368 | self.hourEdit.setObjectName("hourEdit") 369 | self.horizontalLayout_3.addWidget(self.hourEdit) 370 | self.minuteLabel = QtWidgets.QLabel(self.frame_3) 371 | self.minuteLabel.setObjectName("minuteLabel") 372 | self.horizontalLayout_3.addWidget(self.minuteLabel) 373 | self.minEdit = QtWidgets.QLineEdit(self.frame_3) 374 | sizePolicy = QtWidgets.QSizePolicy( 375 | QtWidgets.QSizePolicy.Policy.Expanding, 376 | QtWidgets.QSizePolicy.Policy.Fixed 377 | ) 378 | sizePolicy.setHorizontalStretch(0) 379 | sizePolicy.setVerticalStretch(0) 380 | sizePolicy.setHeightForWidth(self.minEdit.sizePolicy().hasHeightForWidth()) 381 | self.minEdit.setSizePolicy(sizePolicy) 382 | self.minEdit.setObjectName("minEdit") 383 | self.horizontalLayout_3.addWidget(self.minEdit) 384 | self.gridLayout_2.addWidget(self.frame_3, 1, 1, 1, 1) 385 | self.directionLabel = QtWidgets.QLabel(self.expandFrame) 386 | sizePolicy = QtWidgets.QSizePolicy( 387 | QtWidgets.QSizePolicy.Policy.Fixed, 388 | QtWidgets.QSizePolicy.Policy.Maximum 389 | ) 390 | sizePolicy.setHorizontalStretch(0) 391 | sizePolicy.setVerticalStretch(0) 392 | sizePolicy.setHeightForWidth(self.directionLabel.sizePolicy().hasHeightForWidth()) 393 | self.directionLabel.setSizePolicy(sizePolicy) 394 | self.directionLabel.setObjectName("directionLabel") 395 | self.gridLayout_2.addWidget(self.directionLabel, 4, 0, 1, 1) 396 | self.startDayLabel = QtWidgets.QLabel(self.expandFrame) 397 | sizePolicy = QtWidgets.QSizePolicy( 398 | QtWidgets.QSizePolicy.Policy.Fixed, 399 | QtWidgets.QSizePolicy.Policy.Maximum 400 | ) 401 | sizePolicy.setHorizontalStretch(0) 402 | sizePolicy.setVerticalStretch(0) 403 | sizePolicy.setHeightForWidth(self.startDayLabel.sizePolicy().hasHeightForWidth()) 404 | self.startDayLabel.setSizePolicy(sizePolicy) 405 | self.startDayLabel.setObjectName("startDayLabel") 406 | self.gridLayout_2.addWidget(self.startDayLabel, 3, 0, 1, 1) 407 | self.rangeLabel = QtWidgets.QLabel(self.expandFrame) 408 | sizePolicy = QtWidgets.QSizePolicy( 409 | QtWidgets.QSizePolicy.Policy.Fixed, 410 | QtWidgets.QSizePolicy.Policy.Maximum 411 | ) 412 | sizePolicy.setHorizontalStretch(0) 413 | sizePolicy.setVerticalStretch(0) 414 | sizePolicy.setHeightForWidth(self.rangeLabel.sizePolicy().hasHeightForWidth()) 415 | self.rangeLabel.setSizePolicy(sizePolicy) 416 | self.rangeLabel.setObjectName("rangeLabel") 417 | self.gridLayout_2.addWidget(self.rangeLabel, 2, 0, 1, 1) 418 | self.unitLabel = QtWidgets.QLabel(self.expandFrame) 419 | sizePolicy = QtWidgets.QSizePolicy( 420 | QtWidgets.QSizePolicy.Policy.Fixed, 421 | QtWidgets.QSizePolicy.Policy.Maximum 422 | ) 423 | sizePolicy.setHorizontalStretch(0) 424 | sizePolicy.setVerticalStretch(0) 425 | sizePolicy.setHeightForWidth(self.unitLabel.sizePolicy().hasHeightForWidth()) 426 | self.unitLabel.setSizePolicy(sizePolicy) 427 | self.unitLabel.setObjectName("unitLabel") 428 | self.gridLayout_2.addWidget(self.unitLabel, 1, 0, 1, 1) 429 | self.outputLabel = QtWidgets.QLabel(self.expandFrame) 430 | sizePolicy = QtWidgets.QSizePolicy( 431 | QtWidgets.QSizePolicy.Policy.Fixed, 432 | QtWidgets.QSizePolicy.Policy.Maximum 433 | ) 434 | sizePolicy.setHorizontalStretch(0) 435 | sizePolicy.setVerticalStretch(0) 436 | sizePolicy.setHeightForWidth(self.outputLabel.sizePolicy().hasHeightForWidth()) 437 | self.outputLabel.setSizePolicy(sizePolicy) 438 | self.outputLabel.setMinimumSize(QtCore.QSize(82, 0)) 439 | self.outputLabel.setObjectName("outputLabel") 440 | self.gridLayout_2.addWidget(self.outputLabel, 0, 0, 1, 1) 441 | spacerItem = QtWidgets.QSpacerItem( 442 | 24, 443 | 0, 444 | QtWidgets.QSizePolicy.Policy.Fixed, 445 | QtWidgets.QSizePolicy.Policy.Minimum 446 | ) 447 | self.gridLayout_2.addItem(spacerItem, 0, 3, 1, 1) 448 | self.removeButton = HoverButton(self.expandFrame) 449 | sizePolicy = QtWidgets.QSizePolicy( 450 | QtWidgets.QSizePolicy.Policy.Fixed, 451 | QtWidgets.QSizePolicy.Policy.Fixed 452 | ) 453 | sizePolicy.setHorizontalStretch(0) 454 | sizePolicy.setVerticalStretch(0) 455 | sizePolicy.setHeightForWidth(self.removeButton.sizePolicy().hasHeightForWidth()) 456 | self.removeButton.setSizePolicy(sizePolicy) 457 | self.removeButton.setMinimumSize(QtCore.QSize(0, 0)) 458 | self.removeButton.setMaximumSize(QtCore.QSize(20, 20)) 459 | icon5 = QtGui.QIcon() 460 | icon5.addPixmap( 461 | QtGui.QPixmap("../../img/remove_icon.svg"), 462 | QtGui.QIcon.Mode.Normal, 463 | QtGui.QIcon.State.Off 464 | ) 465 | self.removeButton.setIcon(icon5) 466 | self.removeButton.setIconSize(QtCore.QSize(20, 20)) 467 | self.removeButton.setObjectName("removeButton") 468 | self.gridLayout_2.addWidget(self.removeButton, 0, 4, 1, 1) 469 | self.codeButton = HoverButton(self.expandFrame) 470 | sizePolicy = QtWidgets.QSizePolicy( 471 | QtWidgets.QSizePolicy.Policy.Fixed, 472 | QtWidgets.QSizePolicy.Policy.Fixed 473 | ) 474 | sizePolicy.setHorizontalStretch(0) 475 | sizePolicy.setVerticalStretch(0) 476 | sizePolicy.setHeightForWidth(self.codeButton.sizePolicy().hasHeightForWidth()) 477 | self.codeButton.setSizePolicy(sizePolicy) 478 | self.codeButton.setMinimumSize(QtCore.QSize(0, 0)) 479 | self.codeButton.setMaximumSize(QtCore.QSize(20, 20)) 480 | icon6 = QtGui.QIcon() 481 | icon6.addPixmap( 482 | QtGui.QPixmap("../../img/code_icon.svg"), 483 | QtGui.QIcon.Mode.Normal, 484 | QtGui.QIcon.State.Off 485 | ) 486 | self.codeButton.setIcon(icon6) 487 | self.codeButton.setIconSize(QtCore.QSize(20, 20)) 488 | self.codeButton.setObjectName("codeButton") 489 | self.gridLayout_2.addWidget(self.codeButton, 4, 4, 1, 1) 490 | self.codeTextEdit = QtWidgets.QPlainTextEdit(self.expandFrame) 491 | sizePolicy = QtWidgets.QSizePolicy( 492 | QtWidgets.QSizePolicy.Policy.Expanding, 493 | QtWidgets.QSizePolicy.Policy.Preferred 494 | ) 495 | sizePolicy.setHorizontalStretch(0) 496 | sizePolicy.setVerticalStretch(0) 497 | sizePolicy.setHeightForWidth(self.codeTextEdit.sizePolicy().hasHeightForWidth()) 498 | self.codeTextEdit.setSizePolicy(sizePolicy) 499 | self.codeTextEdit.setMaximumSize(QtCore.QSize(16777215, 128)) 500 | self.codeTextEdit.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.SizeAdjustPolicy.AdjustIgnored) 501 | self.codeTextEdit.setTabChangesFocus(False) 502 | self.codeTextEdit.setObjectName("codeTextEdit") 503 | self.gridLayout_2.addWidget(self.codeTextEdit, 5, 0, 1, 5) 504 | self.gridLayout.addWidget(self.expandFrame, 1, 0, 1, 5) 505 | self.verticalLayout.addWidget(self.mainFrame) 506 | 507 | self.retranslateUi(CellWidget) 508 | QtCore.QMetaObject.connectSlotsByName(CellWidget) 509 | 510 | def retranslateUi(self, CellWidget): 511 | _translate = QtCore.QCoreApplication.translate 512 | CellWidget.setWindowTitle(_translate("CellWidget", "Form")) 513 | self.titleColorButton.setToolTip(_translate("CellWidget", "Set a custom title text color.")) 514 | self.titleLabel.setToolTip( 515 | _translate("CellWidget", "Text that appears above or to-the-left-of the output.") 516 | ) 517 | self.titleLabel.setText(_translate("CellWidget", "Title")) 518 | self.startDayDropdown.setToolTip(_translate("CellWidget", "The day a new week should start on.")) 519 | self.startDayDropdown.setCurrentText(_translate("CellWidget", "Sunday")) 520 | self.startDayDropdown.setItemText(0, _translate("CellWidget", "Sunday")) 521 | self.startDayDropdown.setItemText(1, _translate("CellWidget", "Monday")) 522 | self.startDayDropdown.setItemText(2, _translate("CellWidget", "Tuesday")) 523 | self.startDayDropdown.setItemText(3, _translate("CellWidget", "Wednesday")) 524 | self.startDayDropdown.setItemText(4, _translate("CellWidget", "Thursday")) 525 | self.startDayDropdown.setItemText(5, _translate("CellWidget", "Friday")) 526 | self.startDayDropdown.setItemText(6, _translate("CellWidget", "Saturday")) 527 | self.outputColorButton.setToolTip(_translate("CellWidget", "Set a custom output text color.")) 528 | self.rangeDropdown.setToolTip( 529 | _translate("CellWidget", "Time range to filter through for the ranged total stat.") 530 | ) 531 | self.rangeDropdown.setItemText(0, _translate("CellWidget", "Total")) 532 | self.rangeDropdown.setItemText(1, _translate("CellWidget", "Past Week")) 533 | self.rangeDropdown.setItemText(2, _translate("CellWidget", "Past 2 Weeks")) 534 | self.rangeDropdown.setItemText(3, _translate("CellWidget", "Past Month")) 535 | self.rangeDropdown.setItemText(4, _translate("CellWidget", "Past Year")) 536 | self.rangeDropdown.setItemText(5, _translate("CellWidget", "Custom")) 537 | self.calendarCheckbox.setToolTip( 538 | _translate("CellWidget", "Use the start of the selected range instead of using its timespan.") 539 | ) 540 | self.calendarCheckbox.setText(_translate("CellWidget", "Use Calendar Week")) 541 | self.customRangeSpinbox.setToolTip( 542 | _translate("CellWidget", "Amount of days to filter the custom range.") 543 | ) 544 | self.customRangeSpinbox.setSuffix(_translate("CellWidget", " days")) 545 | self.hourLabel.setText(_translate("CellWidget", "Hour")) 546 | self.hourEdit.setText(_translate("CellWidget", "hrs")) 547 | self.minuteLabel.setText(_translate("CellWidget", "Minute")) 548 | self.minEdit.setText(_translate("CellWidget", "min")) 549 | self.directionLabel.setToolTip( 550 | _translate("CellWidget", "Set text-stacking direction to either vertical/horizontal.") 551 | ) 552 | self.directionLabel.setText(_translate("CellWidget", "Direction")) 553 | self.startDayLabel.setToolTip( 554 | _translate("CellWidget", "Day to consider the first day of a calendar week.") 555 | ) 556 | self.startDayLabel.setText(_translate("CellWidget", "Week-Start Day")) 557 | self.rangeLabel.setToolTip( 558 | _translate("CellWidget", "Preset range to use for macros with an expected range.") 559 | ) 560 | self.rangeLabel.setText(_translate("CellWidget", "Selected Range")) 561 | self.unitLabel.setToolTip( 562 | _translate( 563 | "CellWidget", 564 | "Text to be appended to any time-related measurements for all output text." 565 | ) 566 | ) 567 | self.unitLabel.setText(_translate("CellWidget", "Units")) 568 | self.outputLabel.setToolTip( 569 | _translate("CellWidget", "Text that appears below or to-the-right-of the title.") 570 | ) 571 | self.outputLabel.setText(_translate("CellWidget", "Output")) 572 | 573 | 574 | from ..forms import DragHandle, HoverButton, RotateButton 575 | 576 | if __name__ == "__main__": 577 | import sys 578 | 579 | app = QtWidgets.QApplication(sys.argv) 580 | CellWidget = QtWidgets.QWidget() 581 | ui = Ui_CellWidget() 582 | ui.setupUi(CellWidget) 583 | CellWidget.show() 584 | sys.exit(app.exec_()) 585 | -------------------------------------------------------------------------------- /res/ui/Qt6/macro_dialog.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file 'macro_dialog.ui' 4 | # 5 | # Created by: PyQt6 UI code generator 5.15.7 6 | # 7 | # WARNING: Any manual changes made to this file will be lost when pyuic5 is 8 | # run again. Do not edit this file unless you know what you are doing. 9 | 10 | 11 | from PyQt6 import QtCore, QtGui, QtWidgets 12 | 13 | 14 | class Ui_MacroDialog(object): 15 | def setupUi(self, MacroDialog): 16 | MacroDialog.setObjectName("MacroDialog") 17 | MacroDialog.resize(528, 362) 18 | self.verticalLayout = QtWidgets.QVBoxLayout(MacroDialog) 19 | self.verticalLayout.setObjectName("verticalLayout") 20 | self.macroGroupBox = QtWidgets.QGroupBox(MacroDialog) 21 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Expanding) 22 | sizePolicy.setHorizontalStretch(0) 23 | sizePolicy.setVerticalStretch(0) 24 | sizePolicy.setHeightForWidth(self.macroGroupBox.sizePolicy().hasHeightForWidth()) 25 | self.macroGroupBox.setSizePolicy(sizePolicy) 26 | self.macroGroupBox.setObjectName("macroGroupBox") 27 | self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.macroGroupBox) 28 | self.verticalLayout_2.setContentsMargins(6, 0, 6, 6) 29 | self.verticalLayout_2.setObjectName("verticalLayout_2") 30 | self.filterLineEdit = QtWidgets.QLineEdit(self.macroGroupBox) 31 | self.filterLineEdit.setClearButtonEnabled(True) 32 | self.filterLineEdit.setObjectName("filterLineEdit") 33 | self.verticalLayout_2.addWidget(self.filterLineEdit) 34 | self.listView = QtWidgets.QListView(self.macroGroupBox) 35 | self.listView.setFrameShape(QtWidgets.QFrame.Shape.NoFrame) 36 | self.listView.setFrameShadow(QtWidgets.QFrame.Shadow.Plain) 37 | self.listView.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarPolicy.ScrollBarAlwaysOff) 38 | self.listView.setTabKeyNavigation(True) 39 | self.listView.setProperty("showDropIndicator", False) 40 | self.listView.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectionBehavior.SelectRows) 41 | self.listView.setObjectName("listView") 42 | self.verticalLayout_2.addWidget(self.listView) 43 | self.verticalLayout.addWidget(self.macroGroupBox) 44 | self.previewGroupBox = QtWidgets.QGroupBox(MacroDialog) 45 | self.previewGroupBox.setMinimumSize(QtCore.QSize(0, 42)) 46 | self.previewGroupBox.setObjectName("previewGroupBox") 47 | self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.previewGroupBox) 48 | self.verticalLayout_3.setContentsMargins(6, 6, 6, 6) 49 | self.verticalLayout_3.setObjectName("verticalLayout_3") 50 | self.previewLabel = QtWidgets.QLabel(self.previewGroupBox) 51 | self.previewLabel.setObjectName("previewLabel") 52 | self.verticalLayout_3.addWidget(self.previewLabel) 53 | self.verticalLayout.addWidget(self.previewGroupBox) 54 | self.buttonBox = QtWidgets.QDialogButtonBox(MacroDialog) 55 | self.buttonBox.setOrientation(QtCore.Qt.Orientation.Horizontal) 56 | self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.StandardButton.Cancel|QtWidgets.QDialogButtonBox.StandardButton.Ok) 57 | self.buttonBox.setObjectName("buttonBox") 58 | self.verticalLayout.addWidget(self.buttonBox) 59 | 60 | self.retranslateUi(MacroDialog) 61 | self.buttonBox.accepted.connect(MacroDialog.accept) # type: ignore 62 | self.buttonBox.rejected.connect(MacroDialog.reject) # type: ignore 63 | QtCore.QMetaObject.connectSlotsByName(MacroDialog) 64 | 65 | def retranslateUi(self, MacroDialog): 66 | _translate = QtCore.QCoreApplication.translate 67 | MacroDialog.setWindowTitle(_translate("MacroDialog", "Macros")) 68 | self.macroGroupBox.setTitle(_translate("MacroDialog", "Macros")) 69 | self.filterLineEdit.setPlaceholderText(_translate("MacroDialog", "Filter...")) 70 | self.previewGroupBox.setTitle(_translate("MacroDialog", "Preview")) 71 | self.previewLabel.setText(_translate("MacroDialog", "test")) 72 | 73 | 74 | if __name__ == "__main__": 75 | import sys 76 | app = QtWidgets.QApplication(sys.argv) 77 | MacroDialog = QtWidgets.QDialog() 78 | ui = Ui_MacroDialog() 79 | ui.setupUi(MacroDialog) 80 | MacroDialog.show() 81 | sys.exit(app.exec_()) 82 | -------------------------------------------------------------------------------- /res/ui/Qt6/options_dialog.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file 'options_dialog.ui' 4 | # 5 | # Created by: PyQt6 UI code generator 5.15.7 6 | # 7 | # WARNING: Any manual changes made to this file will be lost when pyuic5 is 8 | # run again. Do not edit this file unless you know what you are doing. 9 | 10 | 11 | from PyQt6 import QtCore, QtGui, QtWidgets 12 | 13 | 14 | class Ui_OptionsDialog(object): 15 | def setupUi(self, OptionsDialog): 16 | OptionsDialog.setObjectName("OptionsDialog") 17 | OptionsDialog.setWindowModality(QtCore.Qt.WindowModality.WindowModal) 18 | OptionsDialog.resize(517, 620) 19 | OptionsDialog.setSizeGripEnabled(True) 20 | OptionsDialog.setModal(True) 21 | self.verticalLayout_4 = QtWidgets.QVBoxLayout(OptionsDialog) 22 | self.verticalLayout_4.setObjectName("verticalLayout_4") 23 | self.tabs_widget = QtWidgets.QTabWidget(OptionsDialog) 24 | self.tabs_widget.setEnabled(True) 25 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Expanding) 26 | sizePolicy.setHorizontalStretch(0) 27 | sizePolicy.setVerticalStretch(0) 28 | sizePolicy.setHeightForWidth(self.tabs_widget.sizePolicy().hasHeightForWidth()) 29 | self.tabs_widget.setSizePolicy(sizePolicy) 30 | self.tabs_widget.setTabPosition(QtWidgets.QTabWidget.TabPosition.North) 31 | self.tabs_widget.setTabShape(QtWidgets.QTabWidget.TabShape.Rounded) 32 | self.tabs_widget.setObjectName("tabs_widget") 33 | self.appearance_tab = QtWidgets.QWidget() 34 | self.appearance_tab.setEnabled(True) 35 | self.appearance_tab.setObjectName("appearance_tab") 36 | self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.appearance_tab) 37 | self.verticalLayout_3.setObjectName("verticalLayout_3") 38 | self.frame = QtWidgets.QFrame(self.appearance_tab) 39 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding) 40 | sizePolicy.setHorizontalStretch(0) 41 | sizePolicy.setVerticalStretch(0) 42 | sizePolicy.setHeightForWidth(self.frame.sizePolicy().hasHeightForWidth()) 43 | self.frame.setSizePolicy(sizePolicy) 44 | self.frame.setObjectName("frame") 45 | self.verticalLayout_8 = QtWidgets.QVBoxLayout(self.frame) 46 | self.verticalLayout_8.setObjectName("verticalLayout_8") 47 | self.pages_group = QtWidgets.QGroupBox(self.frame) 48 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Maximum) 49 | sizePolicy.setHorizontalStretch(0) 50 | sizePolicy.setVerticalStretch(0) 51 | sizePolicy.setHeightForWidth(self.pages_group.sizePolicy().hasHeightForWidth()) 52 | self.pages_group.setSizePolicy(sizePolicy) 53 | self.pages_group.setObjectName("pages_group") 54 | self.horizontalLayout = QtWidgets.QHBoxLayout(self.pages_group) 55 | self.horizontalLayout.setObjectName("horizontalLayout") 56 | self.browser_checkbox = QtWidgets.QCheckBox(self.pages_group) 57 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Fixed) 58 | sizePolicy.setHorizontalStretch(0) 59 | sizePolicy.setVerticalStretch(0) 60 | sizePolicy.setHeightForWidth(self.browser_checkbox.sizePolicy().hasHeightForWidth()) 61 | self.browser_checkbox.setSizePolicy(sizePolicy) 62 | self.browser_checkbox.setChecked(True) 63 | self.browser_checkbox.setObjectName("browser_checkbox") 64 | self.horizontalLayout.addWidget(self.browser_checkbox) 65 | self.overview_checkbox = QtWidgets.QCheckBox(self.pages_group) 66 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Fixed) 67 | sizePolicy.setHorizontalStretch(0) 68 | sizePolicy.setVerticalStretch(0) 69 | sizePolicy.setHeightForWidth(self.overview_checkbox.sizePolicy().hasHeightForWidth()) 70 | self.overview_checkbox.setSizePolicy(sizePolicy) 71 | self.overview_checkbox.setChecked(True) 72 | self.overview_checkbox.setObjectName("overview_checkbox") 73 | self.horizontalLayout.addWidget(self.overview_checkbox) 74 | self.congrats_checkbox = QtWidgets.QCheckBox(self.pages_group) 75 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Maximum, QtWidgets.QSizePolicy.Policy.Fixed) 76 | sizePolicy.setHorizontalStretch(0) 77 | sizePolicy.setVerticalStretch(0) 78 | sizePolicy.setHeightForWidth(self.congrats_checkbox.sizePolicy().hasHeightForWidth()) 79 | self.congrats_checkbox.setSizePolicy(sizePolicy) 80 | self.congrats_checkbox.setChecked(True) 81 | self.congrats_checkbox.setObjectName("congrats_checkbox") 82 | self.horizontalLayout.addWidget(self.congrats_checkbox) 83 | self.verticalLayout_8.addWidget(self.pages_group) 84 | self.mainViewGroupbox = QtWidgets.QGroupBox(self.frame) 85 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) 86 | sizePolicy.setHorizontalStretch(0) 87 | sizePolicy.setVerticalStretch(0) 88 | sizePolicy.setHeightForWidth(self.mainViewGroupbox.sizePolicy().hasHeightForWidth()) 89 | self.mainViewGroupbox.setSizePolicy(sizePolicy) 90 | self.mainViewGroupbox.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeading | QtCore.Qt.AlignmentFlag.AlignLeft | QtCore.Qt.AlignmentFlag.AlignTop) 91 | self.mainViewGroupbox.setObjectName("mainViewGroupbox") 92 | self.verticalLayout_5 = QtWidgets.QVBoxLayout(self.mainViewGroupbox) 93 | self.verticalLayout_5.setObjectName("verticalLayout_5") 94 | self.cellListWidget = QtWidgets.QListWidget(self.mainViewGroupbox) 95 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding) 96 | sizePolicy.setHorizontalStretch(0) 97 | sizePolicy.setVerticalStretch(0) 98 | sizePolicy.setHeightForWidth(self.cellListWidget.sizePolicy().hasHeightForWidth()) 99 | self.cellListWidget.setSizePolicy(sizePolicy) 100 | self.cellListWidget.setMinimumSize(QtCore.QSize(0, 64)) 101 | self.cellListWidget.setFrameShape(QtWidgets.QFrame.Shape.NoFrame) 102 | self.cellListWidget.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarPolicy.ScrollBarAlwaysOff) 103 | self.cellListWidget.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.SizeAdjustPolicy.AdjustIgnored) 104 | self.cellListWidget.setEditTriggers(QtWidgets.QAbstractItemView.EditTrigger.NoEditTriggers) 105 | self.cellListWidget.setProperty("showDropIndicator", False) 106 | self.cellListWidget.setDragEnabled(True) 107 | self.cellListWidget.setDragDropMode(QtWidgets.QAbstractItemView.DragDropMode.InternalMove) 108 | self.cellListWidget.setSelectionMode(QtWidgets.QAbstractItemView.SelectionMode.NoSelection) 109 | self.cellListWidget.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollMode.ScrollPerPixel) 110 | self.cellListWidget.setObjectName("cellListWidget") 111 | self.verticalLayout_5.addWidget(self.cellListWidget) 112 | self.verticalLayout_8.addWidget(self.mainViewGroupbox) 113 | self.verticalLayout_3.addWidget(self.frame) 114 | self.tabs_widget.addTab(self.appearance_tab, "") 115 | self.decks_tab = QtWidgets.QWidget() 116 | self.decks_tab.setObjectName("decks_tab") 117 | self.verticalLayout = QtWidgets.QVBoxLayout(self.decks_tab) 118 | self.verticalLayout.setObjectName("verticalLayout") 119 | self.toolbar_checkbox = QtWidgets.QCheckBox(self.decks_tab) 120 | self.toolbar_checkbox.setChecked(True) 121 | self.toolbar_checkbox.setObjectName("toolbar_checkbox") 122 | self.verticalLayout.addWidget(self.toolbar_checkbox) 123 | self.include_deleted_checkbox = QtWidgets.QCheckBox(self.decks_tab) 124 | self.include_deleted_checkbox.setChecked(True) 125 | self.include_deleted_checkbox.setObjectName("include_deleted_checkbox") 126 | self.verticalLayout.addWidget(self.include_deleted_checkbox) 127 | self.useRolloverCheckbox = QtWidgets.QCheckBox(self.decks_tab) 128 | self.useRolloverCheckbox.setChecked(True) 129 | self.useRolloverCheckbox.setObjectName("useRolloverCheckbox") 130 | self.verticalLayout.addWidget(self.useRolloverCheckbox) 131 | self.useDecimalCheckbox = QtWidgets.QCheckBox(self.decks_tab) 132 | self.useDecimalCheckbox.setChecked(True) 133 | self.useDecimalCheckbox.setObjectName("useDecimalCheckbox") 134 | self.verticalLayout.addWidget(self.useDecimalCheckbox) 135 | self.enabled_decks_group = QtWidgets.QGroupBox(self.decks_tab) 136 | self.enabled_decks_group.setMinimumSize(QtCore.QSize(0, 100)) 137 | self.enabled_decks_group.setObjectName("enabled_decks_group") 138 | self.enabled_decks_layout = QtWidgets.QVBoxLayout(self.enabled_decks_group) 139 | self.enabled_decks_layout.setObjectName("enabled_decks_layout") 140 | self.exclude_layout = QtWidgets.QHBoxLayout() 141 | self.exclude_layout.setObjectName("exclude_layout") 142 | self.deck_enable_button = QtWidgets.QPushButton(self.enabled_decks_group) 143 | self.deck_enable_button.setFocusPolicy(QtCore.Qt.FocusPolicy.TabFocus) 144 | self.deck_enable_button.setObjectName("deck_enable_button") 145 | self.exclude_layout.addWidget(self.deck_enable_button) 146 | self.deck_disable_button = QtWidgets.QPushButton(self.enabled_decks_group) 147 | self.deck_disable_button.setFocusPolicy(QtCore.Qt.FocusPolicy.TabFocus) 148 | self.deck_disable_button.setObjectName("deck_disable_button") 149 | self.exclude_layout.addWidget(self.deck_disable_button) 150 | spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) 151 | self.exclude_layout.addItem(spacerItem) 152 | self.enabled_decks_layout.addLayout(self.exclude_layout) 153 | self.excluded_decks_list = QtWidgets.QListWidget(self.enabled_decks_group) 154 | self.excluded_decks_list.setSelectionMode(QtWidgets.QAbstractItemView.SelectionMode.ExtendedSelection) 155 | self.excluded_decks_list.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectionBehavior.SelectRows) 156 | self.excluded_decks_list.setObjectName("excluded_decks_list") 157 | self.enabled_decks_layout.addWidget(self.excluded_decks_list) 158 | self.verticalLayout.addWidget(self.enabled_decks_group) 159 | self.tabs_widget.addTab(self.decks_tab, "") 160 | self.about_tab = QtWidgets.QWidget() 161 | self.about_tab.setObjectName("about_tab") 162 | self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.about_tab) 163 | self.verticalLayout_2.setSizeConstraint(QtWidgets.QLayout.SizeConstraint.SetNoConstraint) 164 | self.verticalLayout_2.setObjectName("verticalLayout_2") 165 | self.scroll_area = QtWidgets.QScrollArea(self.about_tab) 166 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding) 167 | sizePolicy.setHorizontalStretch(0) 168 | sizePolicy.setVerticalStretch(0) 169 | sizePolicy.setHeightForWidth(self.scroll_area.sizePolicy().hasHeightForWidth()) 170 | self.scroll_area.setSizePolicy(sizePolicy) 171 | self.scroll_area.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.SizeAdjustPolicy.AdjustToContents) 172 | self.scroll_area.setWidgetResizable(True) 173 | self.scroll_area.setObjectName("scroll_area") 174 | self.about_scroll = QtWidgets.QWidget() 175 | self.about_scroll.setGeometry(QtCore.QRect(0, 0, 456, 555)) 176 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding) 177 | sizePolicy.setHorizontalStretch(0) 178 | sizePolicy.setVerticalStretch(0) 179 | sizePolicy.setHeightForWidth(self.about_scroll.sizePolicy().hasHeightForWidth()) 180 | self.about_scroll.setSizePolicy(sizePolicy) 181 | self.about_scroll.setObjectName("about_scroll") 182 | self.scroll_layout = QtWidgets.QVBoxLayout(self.about_scroll) 183 | self.scroll_layout.setObjectName("scroll_layout") 184 | self.about_label_header = QtWidgets.QLabel(self.about_scroll) 185 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Preferred) 186 | sizePolicy.setHorizontalStretch(0) 187 | sizePolicy.setVerticalStretch(0) 188 | sizePolicy.setHeightForWidth(self.about_label_header.sizePolicy().hasHeightForWidth()) 189 | self.about_label_header.setSizePolicy(sizePolicy) 190 | self.about_label_header.setTextFormat(QtCore.Qt.TextFormat.AutoText) 191 | self.about_label_header.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeading|QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignTop) 192 | self.about_label_header.setWordWrap(True) 193 | self.about_label_header.setOpenExternalLinks(True) 194 | self.about_label_header.setTextInteractionFlags(QtCore.Qt.TextInteractionFlag.LinksAccessibleByKeyboard|QtCore.Qt.TextInteractionFlag.LinksAccessibleByMouse|QtCore.Qt.TextInteractionFlag.TextBrowserInteraction|QtCore.Qt.TextInteractionFlag.TextSelectableByKeyboard|QtCore.Qt.TextInteractionFlag.TextSelectableByMouse) 195 | self.about_label_header.setObjectName("about_label_header") 196 | self.scroll_layout.addWidget(self.about_label_header) 197 | self.supportButtonHolder = QtWidgets.QFrame(self.about_scroll) 198 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Maximum) 199 | sizePolicy.setHorizontalStretch(0) 200 | sizePolicy.setVerticalStretch(0) 201 | sizePolicy.setHeightForWidth(self.supportButtonHolder.sizePolicy().hasHeightForWidth()) 202 | self.supportButtonHolder.setSizePolicy(sizePolicy) 203 | self.supportButtonHolder.setObjectName("supportButtonHolder") 204 | self.support_buttons = QtWidgets.QHBoxLayout(self.supportButtonHolder) 205 | self.support_buttons.setContentsMargins(6, 6, 6, 6) 206 | self.support_buttons.setObjectName("support_buttons") 207 | self.like_button = QtWidgets.QPushButton(self.supportButtonHolder) 208 | self.like_button.setMinimumSize(QtCore.QSize(0, 42)) 209 | self.like_button.setMaximumSize(QtCore.QSize(200, 16777215)) 210 | self.like_button.setCursor(QtGui.QCursor(QtCore.Qt.CursorShape.PointingHandCursor)) 211 | self.like_button.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus) 212 | self.like_button.setContextMenuPolicy(QtCore.Qt.ContextMenuPolicy.CustomContextMenu) 213 | icon = QtGui.QIcon() 214 | icon.addPixmap(QtGui.QPixmap("res/img/anki_like.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off) 215 | self.like_button.setIcon(icon) 216 | self.like_button.setIconSize(QtCore.QSize(32, 32)) 217 | self.like_button.setObjectName("like_button") 218 | self.support_buttons.addWidget(self.like_button) 219 | self.patreon_button = QtWidgets.QPushButton(self.supportButtonHolder) 220 | self.patreon_button.setMinimumSize(QtCore.QSize(0, 42)) 221 | self.patreon_button.setMaximumSize(QtCore.QSize(200, 16777215)) 222 | self.patreon_button.setCursor(QtGui.QCursor(QtCore.Qt.CursorShape.PointingHandCursor)) 223 | self.patreon_button.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus) 224 | self.patreon_button.setContextMenuPolicy(QtCore.Qt.ContextMenuPolicy.CustomContextMenu) 225 | icon1 = QtGui.QIcon() 226 | icon1.addPixmap(QtGui.QPixmap("res/img/patreon.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off) 227 | self.patreon_button.setIcon(icon1) 228 | self.patreon_button.setIconSize(QtCore.QSize(32, 32)) 229 | self.patreon_button.setObjectName("patreon_button") 230 | self.support_buttons.addWidget(self.patreon_button) 231 | self.kofi_button = QtWidgets.QPushButton(self.supportButtonHolder) 232 | self.kofi_button.setMinimumSize(QtCore.QSize(0, 42)) 233 | self.kofi_button.setMaximumSize(QtCore.QSize(200, 16777215)) 234 | self.kofi_button.setCursor(QtGui.QCursor(QtCore.Qt.CursorShape.PointingHandCursor)) 235 | self.kofi_button.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus) 236 | self.kofi_button.setContextMenuPolicy(QtCore.Qt.ContextMenuPolicy.CustomContextMenu) 237 | icon2 = QtGui.QIcon() 238 | icon2.addPixmap(QtGui.QPixmap("res/img/kofilogo_blue.PNG"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off) 239 | self.kofi_button.setIcon(icon2) 240 | self.kofi_button.setIconSize(QtCore.QSize(32, 32)) 241 | self.kofi_button.setObjectName("kofi_button") 242 | self.support_buttons.addWidget(self.kofi_button) 243 | self.scroll_layout.addWidget(self.supportButtonHolder) 244 | self.about_label_body = QtWidgets.QLabel(self.about_scroll) 245 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Expanding) 246 | sizePolicy.setHorizontalStretch(0) 247 | sizePolicy.setVerticalStretch(0) 248 | sizePolicy.setHeightForWidth(self.about_label_body.sizePolicy().hasHeightForWidth()) 249 | self.about_label_body.setSizePolicy(sizePolicy) 250 | self.about_label_body.setTextFormat(QtCore.Qt.TextFormat.AutoText) 251 | self.about_label_body.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeading|QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignTop) 252 | self.about_label_body.setWordWrap(True) 253 | self.about_label_body.setOpenExternalLinks(True) 254 | self.about_label_body.setTextInteractionFlags(QtCore.Qt.TextInteractionFlag.LinksAccessibleByKeyboard|QtCore.Qt.TextInteractionFlag.LinksAccessibleByMouse|QtCore.Qt.TextInteractionFlag.TextBrowserInteraction|QtCore.Qt.TextInteractionFlag.TextSelectableByKeyboard|QtCore.Qt.TextInteractionFlag.TextSelectableByMouse) 255 | self.about_label_body.setObjectName("about_label_body") 256 | self.scroll_layout.addWidget(self.about_label_body) 257 | self.scroll_area.setWidget(self.about_scroll) 258 | self.verticalLayout_2.addWidget(self.scroll_area) 259 | self.tabs_widget.addTab(self.about_tab, "") 260 | self.verticalLayout_4.addWidget(self.tabs_widget) 261 | self.frame_2 = QtWidgets.QFrame(OptionsDialog) 262 | self.frame_2.setObjectName("frame_2") 263 | self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.frame_2) 264 | self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0) 265 | self.horizontalLayout_2.setObjectName("horizontalLayout_2") 266 | self.confirm_button_box = QtWidgets.QDialogButtonBox(self.frame_2) 267 | self.confirm_button_box.setOrientation(QtCore.Qt.Orientation.Horizontal) 268 | self.confirm_button_box.setStandardButtons(QtWidgets.QDialogButtonBox.StandardButton.Apply|QtWidgets.QDialogButtonBox.StandardButton.Cancel|QtWidgets.QDialogButtonBox.StandardButton.Ok|QtWidgets.QDialogButtonBox.StandardButton.RestoreDefaults) 269 | self.confirm_button_box.setObjectName("confirm_button_box") 270 | self.horizontalLayout_2.addWidget(self.confirm_button_box) 271 | self.supportButton = HoverButton(self.frame_2) 272 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Fixed, QtWidgets.QSizePolicy.Policy.Fixed) 273 | sizePolicy.setHorizontalStretch(0) 274 | sizePolicy.setVerticalStretch(0) 275 | sizePolicy.setHeightForWidth(self.supportButton.sizePolicy().hasHeightForWidth()) 276 | self.supportButton.setSizePolicy(sizePolicy) 277 | self.supportButton.setObjectName("supportButton") 278 | self.horizontalLayout_2.addWidget(self.supportButton) 279 | self.verticalLayout_4.addWidget(self.frame_2) 280 | 281 | self.retranslateUi(OptionsDialog) 282 | self.tabs_widget.setCurrentIndex(0) 283 | self.confirm_button_box.accepted.connect(OptionsDialog.accept) # type: ignore 284 | self.confirm_button_box.rejected.connect(OptionsDialog.reject) # type: ignore 285 | QtCore.QMetaObject.connectSlotsByName(OptionsDialog) 286 | 287 | def retranslateUi(self, OptionsDialog): 288 | _translate = QtCore.QCoreApplication.translate 289 | OptionsDialog.setWindowTitle(_translate("OptionsDialog", "Study Time Stats Options")) 290 | self.pages_group.setTitle(_translate("OptionsDialog", "Enabled Pages")) 291 | self.browser_checkbox.setToolTip(_translate("OptionsDialog", "The main page for browsing decks.")) 292 | self.browser_checkbox.setText(_translate("OptionsDialog", "Deck Browser")) 293 | self.overview_checkbox.setToolTip(_translate("OptionsDialog", "The page that shows when viewing a deck.")) 294 | self.overview_checkbox.setText(_translate("OptionsDialog", "Overview")) 295 | self.congrats_checkbox.setToolTip(_translate("OptionsDialog", "The page that shows when viewing a deck that has its reviews done for the day.")) 296 | self.congrats_checkbox.setText(_translate("OptionsDialog", "Congrats")) 297 | self.mainViewGroupbox.setTitle(_translate("OptionsDialog", "Stats View")) 298 | self.cellListWidget.setSortingEnabled(True) 299 | self.tabs_widget.setTabText(self.tabs_widget.indexOf(self.appearance_tab), _translate("OptionsDialog", "General")) 300 | self.toolbar_checkbox.setToolTip(_translate("OptionsDialog", "Enable the Tools Menu shortcut for these options. \n" 301 | "(Can also be accessed via Tools>Add-ons>Study Time Stats>Config)")) 302 | self.toolbar_checkbox.setText(_translate("OptionsDialog", "Show options shortcut in the Tools Menu")) 303 | self.include_deleted_checkbox.setToolTip(_translate("OptionsDialog", "Include review/time stats for cards that\'ve been deleted.")) 304 | self.include_deleted_checkbox.setText(_translate("OptionsDialog", "Include reviews from deleted cards")) 305 | self.useRolloverCheckbox.setToolTip(_translate("OptionsDialog", "Use Anki\'s rollover (next-day) hour when considering the time days are cut off.")) 306 | self.useRolloverCheckbox.setText(_translate("OptionsDialog", "Use the next-day hour when calculating ranged times")) 307 | self.useDecimalCheckbox.setToolTip(_translate("OptionsDialog", "Shows an \"hour.min\" format for all time outputs. \n" 308 | "(Otherwise uses \"hh:mm\")")) 309 | self.useDecimalCheckbox.setText(_translate("OptionsDialog", "Use decimal format time outputs")) 310 | self.enabled_decks_group.setTitle(_translate("OptionsDialog", "Enabled Decks")) 311 | self.deck_enable_button.setToolTip(_translate("OptionsDialog", "Enable stats for the selected deck(s).")) 312 | self.deck_enable_button.setText(_translate("OptionsDialog", "Enable")) 313 | self.deck_disable_button.setToolTip(_translate("OptionsDialog", "Disable stats for the selected deck(s).")) 314 | self.deck_disable_button.setText(_translate("OptionsDialog", "Disable")) 315 | self.tabs_widget.setTabText(self.tabs_widget.indexOf(self.decks_tab), _translate("OptionsDialog", "Advanced")) 316 | self.about_label_header.setText(_translate("OptionsDialog", "##

Study Time Stats

\n" 317 | "Adds a total and ranged study time statistic to Anki\'s main window. \n" 318 | "\n" 319 | "Version: {version} \n" 320 | "Have any issues or feedback? Feel free to post on the project\'s issue section on [GitHub](https://github.com/iamjustkoi/StudyTimeStats/issues)! \n" 321 | "\n" 322 | "[Releases/Changelog](https://github.com/iamjustkoi/StudyTimeStats/releases) \n" 323 | "[Source Code](https://github.com/iamjustkoi/StudyTimeStats) \n" 324 | "\n" 325 | "If you like the add-on and want to consider supporting me in anyway:")) 326 | self.like_button.setToolTip(_translate("OptionsDialog", "Leave a review over at AnkiWeb!")) 327 | self.like_button.setText(_translate("OptionsDialog", "AnkiWeb ")) 328 | self.patreon_button.setToolTip(_translate("OptionsDialog", "Follow/support me on Patreon!")) 329 | self.patreon_button.setText(_translate("OptionsDialog", " Patreon ")) 330 | self.kofi_button.setToolTip(_translate("OptionsDialog", "Buy me a coffee with Ko-Fi!")) 331 | self.kofi_button.setText(_translate("OptionsDialog", "Ko-Fi")) 332 | self.about_label_body.setText(_translate("OptionsDialog", "Every bit helps and is greatly appreciated! <3\n" 333 | "\n" 334 | "### Text Macros\n" 335 | "All output text can also be filtered to show some more customized information (e.g. \"Past %range\" to \"Past Week\"). These can be used multiple times and will update whenever Anki\'s main window reloads. \n" 336 | "\n" 337 | "`%%` - can be used to return a single % symbol and disable filtering for any macro text (e.g. `%%` -> %, `%%range` -> %range)\n" 338 | "\n" 339 | "*Small warning: as a general rule, the more stats used/the larger the range of the stat, the longer it might take to load them all (some caching is also done on the side, too though).\n" 340 | "\n" 341 | "

\n" 342 | "

\n" 343 | "Thanks for downloading and hope you enjoy!\n" 344 | "\n" 345 | "-koi \n" 346 | "\n" 347 | "\n" 348 | "

\n" 349 | "

\n" 350 | "MIT License \n" 351 | "\n" 352 | "©2022-2023 JustKoi (iamjustkoi)" 353 | ) 354 | ) 355 | self.tabs_widget.setTabText(self.tabs_widget.indexOf(self.about_tab), _translate("OptionsDialog", "About")) 356 | self.supportButton.setText(_translate("OptionsDialog", "<3")) 357 | 358 | 359 | from ..forms import HoverButton 360 | 361 | if __name__ == "__main__": 362 | import sys 363 | 364 | app = QtWidgets.QApplication(sys.argv) 365 | OptionsDialog = QtWidgets.QDialog() 366 | ui = Ui_OptionsDialog() 367 | ui.setupUi(OptionsDialog) 368 | OptionsDialog.show() 369 | sys.exit(app.exec_()) 370 | -------------------------------------------------------------------------------- /res/ui/cell_item.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | CellWidget 4 | 5 | 6 | 7 | 0 8 | 0 9 | 460 10 | 329 11 | 12 | 13 | 14 | 15 | 0 16 | 0 17 | 18 | 19 | 20 | Form 21 | 22 | 23 | 24 | 0 25 | 26 | 27 | 6 28 | 29 | 30 | 0 31 | 32 | 33 | 6 34 | 35 | 36 | 6 37 | 38 | 39 | 40 | 41 | 42 | 0 43 | 0 44 | 45 | 46 | 47 | 48 | 0 49 | 64 50 | 51 | 52 | 53 | #addButton { 54 | border: 1px solid rgba(134, 134, 134, 128); 55 | border-radius: 2px; 56 | background: transparent; 57 | } 58 | 59 | 60 | 61 | ../img/add_icon.svg../img/add_icon.svg 62 | 63 | 64 | 65 | 20 66 | 20 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 0 76 | 0 77 | 78 | 79 | 80 | #mainFrame { 81 | background: rgba(163, 163, 163, 5%); 82 | border-radius: 6px; 83 | } 84 | 85 | 86 | 87 | 88 | 89 | 90 | 0 91 | 0 92 | 93 | 94 | 95 | 96 | 20 97 | 20 98 | 99 | 100 | 101 | Set a custom title text color. 102 | 103 | 104 | #titleColorButton { 105 | border-radius: 10px; 106 | background-color: #76bfb4; 107 | width: 20px; 108 | } 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 0 120 | 0 121 | 122 | 123 | 124 | 125 | 82 126 | 0 127 | 128 | 129 | 130 | Text that appears above or to-the-left-of the output. 131 | 132 | 133 | #mainFrame { 134 | background: rgba(163, 163, 163, 5%); 135 | border-radius: 6px; 136 | } 137 | 138 | 139 | Title 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 0 148 | 0 149 | 150 | 151 | 152 | 153 | 20 154 | 20 155 | 156 | 157 | 158 | OpenHandCursor 159 | 160 | 161 | true 162 | 163 | 164 | 165 | ../img/vert_grip.svg../img/vert_grip.svg 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 0 174 | 0 175 | 176 | 177 | 178 | 179 | 20 180 | 20 181 | 182 | 183 | 184 | 185 | ../img/chevron_down.svg../img/chevron_down.svg 186 | 187 | 188 | 189 | 20 190 | 20 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 0 200 | 0 201 | 202 | 203 | 204 | 205 | 0 206 | 207 | 208 | 0 209 | 210 | 211 | 0 212 | 213 | 214 | 0 215 | 216 | 217 | 218 | 219 | 220 | 0 221 | 0 222 | 223 | 224 | 225 | Qt::StrongFocus 226 | 227 | 228 | The day a new week should start on. 229 | 230 | 231 | Sunday 232 | 233 | 234 | QComboBox::AdjustToContents 235 | 236 | 237 | 238 | Sunday 239 | 240 | 241 | 242 | 243 | Monday 244 | 245 | 246 | 247 | 248 | Tuesday 249 | 250 | 251 | 252 | 253 | Wednesday 254 | 255 | 256 | 257 | 258 | Thursday 259 | 260 | 261 | 262 | 263 | Friday 264 | 265 | 266 | 267 | 268 | Saturday 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 0 281 | 0 282 | 283 | 284 | 285 | 286 | 20 287 | 20 288 | 289 | 290 | 291 | Set a custom output text color. 292 | 293 | 294 | #outputColorButton { 295 | border-radius: 10px; 296 | background-color: #FFF; 297 | width: 20px; 298 | } 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 0 307 | 0 308 | 309 | 310 | 311 | QFrame::NoFrame 312 | 313 | 314 | QFrame::Raised 315 | 316 | 317 | 318 | 0 319 | 320 | 321 | 0 322 | 323 | 324 | 0 325 | 326 | 327 | 0 328 | 329 | 330 | 331 | 332 | 333 | 0 334 | 0 335 | 336 | 337 | 338 | Qt::StrongFocus 339 | 340 | 341 | Time range to filter through for the ranged total stat. 342 | 343 | 344 | QComboBox::AdjustToContents 345 | 346 | 347 | 348 | Total 349 | 350 | 351 | 352 | 353 | Past Week 354 | 355 | 356 | 357 | 358 | Past 2 Weeks 359 | 360 | 361 | 362 | 363 | Past Month 364 | 365 | 366 | 367 | 368 | Past Year 369 | 370 | 371 | 372 | 373 | Custom 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 0 383 | 0 384 | 385 | 386 | 387 | QFrame::NoFrame 388 | 389 | 390 | QFrame::Raised 391 | 392 | 393 | 394 | 0 395 | 396 | 397 | 0 398 | 399 | 400 | 0 401 | 402 | 403 | 0 404 | 405 | 406 | 0 407 | 408 | 409 | 410 | 411 | 412 | 0 413 | 0 414 | 415 | 416 | 417 | Use the start of the selected range instead of using its timespan. 418 | 419 | 420 | Use Calendar Week 421 | 422 | 423 | true 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 0 432 | 0 433 | 434 | 435 | 436 | Qt::StrongFocus 437 | 438 | 439 | Amount of days to filter the custom range. 440 | 441 | 442 | days 443 | 444 | 445 | 5690 446 | 447 | 448 | 7 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 0 463 | 0 464 | 465 | 466 | 467 | QFrame::NoFrame 468 | 469 | 470 | QFrame::Plain 471 | 472 | 473 | 474 | 0 475 | 476 | 477 | 0 478 | 479 | 480 | 0 481 | 482 | 483 | 0 484 | 485 | 486 | 0 487 | 488 | 489 | 490 | 491 | false 492 | 493 | 494 | 495 | 0 496 | 0 497 | 498 | 499 | 500 | 501 | 42 502 | 16777215 503 | 504 | 505 | 506 | 507 | ../img/vert_lines.svg../img/vert_lines.svg 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 0 516 | 0 517 | 518 | 519 | 520 | 521 | 42 522 | 16777215 523 | 524 | 525 | 526 | 527 | ../img/horiz_lines.svg../img/horiz_lines.svg 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 0 539 | 0 540 | 541 | 542 | 543 | QFrame::NoFrame 544 | 545 | 546 | QFrame::Raised 547 | 548 | 549 | 550 | 0 551 | 552 | 553 | 0 554 | 555 | 556 | 0 557 | 558 | 559 | 0 560 | 561 | 562 | 563 | 564 | Hour 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | 0 573 | 0 574 | 575 | 576 | 577 | hrs 578 | 579 | 580 | 581 | 582 | 583 | 584 | Minute 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 0 593 | 0 594 | 595 | 596 | 597 | min 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 0 609 | 0 610 | 611 | 612 | 613 | Set text-stacking direction to either vertical/horizontal. 614 | 615 | 616 | Direction 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 0 625 | 0 626 | 627 | 628 | 629 | Day to consider the first day of a calendar week. 630 | 631 | 632 | Week-Start Day 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 0 641 | 0 642 | 643 | 644 | 645 | Preset range to use for macros with an expected range. 646 | 647 | 648 | Selected Range 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 0 657 | 0 658 | 659 | 660 | 661 | Text to be appended to any time-related measurements for all output text. 662 | 663 | 664 | Units 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 0 673 | 0 674 | 675 | 676 | 677 | 678 | 82 679 | 0 680 | 681 | 682 | 683 | Text that appears below or to-the-right-of the title. 684 | 685 | 686 | Output 687 | 688 | 689 | 690 | 691 | 692 | 693 | Qt::Horizontal 694 | 695 | 696 | QSizePolicy::Fixed 697 | 698 | 699 | 700 | 24 701 | 0 702 | 703 | 704 | 705 | 706 | 707 | 708 | 709 | 710 | 0 711 | 0 712 | 713 | 714 | 715 | 716 | 0 717 | 0 718 | 719 | 720 | 721 | 722 | 20 723 | 20 724 | 725 | 726 | 727 | 728 | ../img/remove_icon.svg../img/remove_icon.svg 729 | 730 | 731 | 732 | 20 733 | 20 734 | 735 | 736 | 737 | 738 | 739 | 740 | 741 | 742 | 0 743 | 0 744 | 745 | 746 | 747 | 748 | 0 749 | 0 750 | 751 | 752 | 753 | 754 | 20 755 | 20 756 | 757 | 758 | 759 | 760 | ../img/code_icon.svg../img/code_icon.svg 761 | 762 | 763 | 764 | 20 765 | 20 766 | 767 | 768 | 769 | 770 | 771 | 772 | 773 | 774 | 0 775 | 0 776 | 777 | 778 | 779 | 780 | 16777215 781 | 128 782 | 783 | 784 | 785 | QAbstractScrollArea::AdjustIgnored 786 | 787 | 788 | false 789 | 790 | 791 | 792 | 793 | 794 | 795 | 796 | 797 | 798 | 799 | 800 | 801 | 802 | HoverButton 803 | QPushButton 804 |
.forms.h
805 |
806 | 807 | DragHandle 808 | QPushButton 809 |
.forms.h
810 |
811 | 812 | RotateButton 813 | QPushButton 814 |
.forms.h
815 |
816 |
817 | 818 | 819 |
820 | -------------------------------------------------------------------------------- /res/ui/forms.py: -------------------------------------------------------------------------------- 1 | from aqt import Qt 2 | from aqt.qt import ( 3 | QToolButton, 4 | QColor, 5 | QShowEvent, 6 | QIcon, 7 | QTransform, 8 | QMouseEvent, 9 | QListWidget, 10 | QT_VERSION_STR, 11 | ) 12 | 13 | ANKI_QT_VER = int(QT_VERSION_STR.split('.')[0]) 14 | 15 | if ANKI_QT_VER == 5: 16 | MaskOutColor = Qt.MaskMode.MaskOutColor 17 | ClosedHandCursor = Qt.CursorShape.ClosedHandCursor 18 | OpenHandCursor = Qt.CursorShape.OpenHandCursor 19 | SmoothTransformation = Qt.TransformationMode.SmoothTransformation 20 | else: 21 | MaskOutColor = Qt.MaskMode.MaskOutColor 22 | ClosedHandCursor = Qt.CursorShape.ClosedHandCursor 23 | OpenHandCursor = Qt.CursorShape.OpenHandCursor 24 | SmoothTransformation = Qt.TransformationMode.SmoothTransformation 25 | 26 | 27 | class HoverButton(QToolButton): 28 | """ 29 | Custom button for handling button icon tinting during hover events with QPushButtons. 30 | """ 31 | 32 | tint = "#FFFFFF" 33 | hover_tint = "#8a8a8a" 34 | mask_color = "black" 35 | raw_icon = None 36 | locked = False 37 | 38 | def setMaskColor(self, color: str): 39 | self.mask_color = color 40 | 41 | def setTint(self, tint: str): 42 | self.tint = tint 43 | 44 | def setHoverTint(self, tint: str): 45 | """ 46 | Set the color of the icon when the mouse is hovering over the button. 47 | :param tint: a string representation of the color 48 | """ 49 | self.hover_tint = tint 50 | 51 | def setRawIcon(self, icon: QIcon) -> None: 52 | self.raw_icon = icon 53 | 54 | def showEvent(self, a0: QShowEvent) -> None: 55 | super().showEvent(a0) 56 | self._updateIcon(False) 57 | 58 | def enterEvent(self, *args, **kwargs): 59 | super().enterEvent(*args, *kwargs) 60 | self._updateIcon(True) 61 | 62 | def leaveEvent(self, *args, **kwargs): 63 | super().leaveEvent(*args, *kwargs) 64 | if not self.locked: 65 | self._updateIcon(False) 66 | 67 | def lockHoverTint(self, locked: bool = True): 68 | self.locked = locked 69 | 70 | def _updateIcon(self, is_hovered: bool): 71 | """ 72 | Updates the icon of the HoverButton to a tinted color if the mouse is currently hovering over it. 73 | 74 | :param is_hovered: whether the mouse is currently hovered over the HoverButton 75 | """ 76 | 77 | pixmap = self.raw_icon.pixmap(self.size(), QIcon.Mode.Normal, QIcon.State.On) 78 | 79 | if is_hovered: 80 | mask = pixmap.createMaskFromColor(QColor(self.mask_color), MaskOutColor) 81 | pixmap.fill(QColor(self.hover_tint)) 82 | pixmap.setMask(mask) 83 | self.setIcon(QIcon(pixmap)) 84 | 85 | else: 86 | mask = pixmap.createMaskFromColor(QColor(self.mask_color), MaskOutColor) 87 | pixmap.fill(QColor(self.tint)) 88 | pixmap.setMask(mask) 89 | self.setIcon(QIcon(pixmap)) 90 | 91 | self.adjustSize() 92 | self.setMinimumSize(self.sizeHint()) 93 | 94 | 95 | class RotateButton(QToolButton): 96 | rotation = 0 97 | tint = "#FFFFFF" 98 | mask_color = "black" 99 | raw_icon = None 100 | 101 | def setRotation(self, degrees: float): 102 | self.rotation = degrees 103 | self._updateIcon() 104 | 105 | def setRawIcon(self, icon: QIcon) -> None: 106 | self.raw_icon = icon 107 | 108 | def showEvent(self, evt: QShowEvent) -> None: 109 | super().showEvent(evt) 110 | self._updateIcon() 111 | 112 | def setMaskColor(self, color: str): 113 | self.mask_color = color 114 | 115 | def setTint(self, tint: str): 116 | self.tint = tint 117 | 118 | def _updateIcon(self): 119 | """ 120 | Updates the icon of the RotateButton to the tinted color, using the stored mask color. 121 | """ 122 | pixmap = self.raw_icon.pixmap(self.size(), QIcon.Mode.Normal, QIcon.State.On)\ 123 | .transformed(QTransform().rotate(self.rotation), SmoothTransformation) 124 | mask = pixmap.createMaskFromColor(QColor(self.mask_color), MaskOutColor) 125 | pixmap.fill(QColor(self.tint)) 126 | pixmap.setMask(mask) 127 | 128 | self.setIcon(QIcon(pixmap)) 129 | 130 | self.adjustSize() 131 | self.setMinimumSize(self.sizeHint()) 132 | 133 | 134 | class DragHandle(QToolButton): 135 | start_pos = None 136 | last_drag_global_pos = None 137 | list_widget: QListWidget = None 138 | is_dragging = False 139 | list_item = None 140 | icon_color = '#FFFFFF' 141 | last_target_idx = -1 142 | padding = 4 143 | 144 | def __init__(self, *args): 145 | super().__init__(*args) 146 | self.setCursor(OpenHandCursor) 147 | 148 | def setIcon(self, icon: QIcon) -> None: 149 | pixmap = icon.pixmap(self.size(), QIcon.Mode.Normal, QIcon.State.On) 150 | mask = pixmap.createMaskFromColor(QColor('black'), MaskOutColor) 151 | pixmap.fill(QColor(self.icon_color)) 152 | pixmap.setMask(mask) 153 | super().setIcon(QIcon(pixmap)) 154 | 155 | self.adjustSize() 156 | self.setMinimumSize(self.sizeHint()) 157 | 158 | def mousePressEvent(self, event: QMouseEvent) -> None: 159 | self.start_pos = event.pos() 160 | 161 | def mouseMoveEvent(self, event: QMouseEvent) -> None: 162 | offset = self.start_pos.y() - event.pos().y() if self.start_pos else 0 163 | 164 | if abs(offset) > 5: 165 | # Moved beyond range (enough to consider a drag check/updates) 166 | self.drag(offset) 167 | 168 | def mouseReleaseEvent(self, event: QMouseEvent) -> None: 169 | self.is_dragging = False 170 | self.start_pos = None 171 | self.setCursor(OpenHandCursor) 172 | 173 | def drag(self, offset: int): 174 | if not self.is_dragging: 175 | self.is_dragging = True 176 | self.setCursor(ClosedHandCursor) 177 | 178 | # Offset greater than half of the currently dragged cell item's height (plus padding) 179 | if self.list_widget and self.list_item and abs(offset) > ((self.parentWidget().height() / 2) + self.padding): 180 | drag_idx = self.list_widget.row(self.list_item) 181 | target_idx = drag_idx + (-1 if offset > 0 else 1) 182 | 183 | move_dir = target_idx - drag_idx 184 | 185 | if target_idx != self.list_widget.count() - 1: 186 | # Target index isn't the last cell item (empty cell) 187 | target_item = self.list_widget.item(target_idx) 188 | 189 | if target_item: 190 | did_switch_dir = False 191 | if self.last_drag_global_pos: 192 | current_drag_global_pos = self.parentWidget().cursor().pos() 193 | global_dir_y = current_drag_global_pos.y() - self.last_drag_global_pos.y() 194 | 195 | # Moving in the opposite direction from the last drag event 196 | did_switch_dir = (global_dir_y < 0 < -move_dir) or (global_dir_y > 0 > -move_dir) 197 | 198 | # noinspection PyUnresolvedReferences 199 | target_cell = target_item.cell_item 200 | 201 | # Not the same target, or the same target and moving in the opposite direction 202 | if target_cell.index != self.last_target_idx or did_switch_dir: 203 | # Swap indices 204 | self.list_item.cell_item.index = target_idx 205 | target_cell.index = drag_idx 206 | 207 | # Re-sort post index swap 208 | self.list_widget.sortItems() 209 | 210 | # Broadcast change 211 | self.list_widget.currentRowChanged.emit(self.list_item.cell_item.index) 212 | 213 | # Cache values 214 | self.last_target_idx = drag_idx 215 | self.last_drag_global_pos = self.parentWidget().cursor().pos() 216 | -------------------------------------------------------------------------------- /res/ui/macro_dialog.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MacroDialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 528 10 | 362 11 | 12 | 13 | 14 | Macros 15 | 16 | 17 | 18 | 19 | 20 | 21 | 0 22 | 0 23 | 24 | 25 | 26 | Macros 27 | 28 | 29 | 30 | 6 31 | 32 | 33 | 0 34 | 35 | 36 | 6 37 | 38 | 39 | 6 40 | 41 | 42 | 43 | 44 | Filter... 45 | 46 | 47 | true 48 | 49 | 50 | 51 | 52 | 53 | 54 | QFrame::NoFrame 55 | 56 | 57 | QFrame::Plain 58 | 59 | 60 | Qt::ScrollBarAlwaysOff 61 | 62 | 63 | true 64 | 65 | 66 | false 67 | 68 | 69 | QAbstractItemView::SelectRows 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 0 81 | 42 82 | 83 | 84 | 85 | Preview 86 | 87 | 88 | 89 | 6 90 | 91 | 92 | 6 93 | 94 | 95 | 6 96 | 97 | 98 | 6 99 | 100 | 101 | 102 | 103 | test 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | Qt::Horizontal 114 | 115 | 116 | QDialogButtonBox::Cancel|QDialogButtonBox::Ok 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | buttonBox 126 | accepted() 127 | MacroDialog 128 | accept() 129 | 130 | 131 | 248 132 | 254 133 | 134 | 135 | 157 136 | 274 137 | 138 | 139 | 140 | 141 | buttonBox 142 | rejected() 143 | MacroDialog 144 | reject() 145 | 146 | 147 | 316 148 | 260 149 | 150 | 151 | 286 152 | 274 153 | 154 | 155 | 156 | 157 | 158 | -------------------------------------------------------------------------------- /res/ui/options_dialog.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | JustKoi 4 | OptionsDialog 5 | 6 | 7 | Qt::WindowModal 8 | 9 | 10 | 11 | 0 12 | 0 13 | 517 14 | 620 15 | 16 | 17 | 18 | Study Time Stats Options 19 | 20 | 21 | true 22 | 23 | 24 | true 25 | 26 | 27 | 28 | 29 | 30 | true 31 | 32 | 33 | 34 | 0 35 | 0 36 | 37 | 38 | 39 | QTabWidget::North 40 | 41 | 42 | QTabWidget::Rounded 43 | 44 | 45 | 0 46 | 47 | 48 | 49 | true 50 | 51 | 52 | General 53 | 54 | 55 | 56 | 57 | 58 | 59 | 0 60 | 0 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 0 69 | 0 70 | 71 | 72 | 73 | Enabled Pages 74 | 75 | 76 | 77 | 78 | 79 | 80 | 0 81 | 0 82 | 83 | 84 | 85 | The main page for browsing decks. 86 | 87 | 88 | Deck Browser 89 | 90 | 91 | true 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 0 100 | 0 101 | 102 | 103 | 104 | The page that shows when viewing a deck. 105 | 106 | 107 | Overview 108 | 109 | 110 | true 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 0 119 | 0 120 | 121 | 122 | 123 | The page that shows when viewing a deck that has its reviews done for the day. 124 | 125 | 126 | Congrats 127 | 128 | 129 | true 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 0 141 | 0 142 | 143 | 144 | 145 | Stats View 146 | 147 | 148 | Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop 149 | 150 | 151 | 152 | 153 | 154 | 155 | 0 156 | 0 157 | 158 | 159 | 160 | 161 | 0 162 | 64 163 | 164 | 165 | 166 | QFrame::NoFrame 167 | 168 | 169 | Qt::ScrollBarAlwaysOff 170 | 171 | 172 | QAbstractScrollArea::AdjustIgnored 173 | 174 | 175 | QAbstractItemView::NoEditTriggers 176 | 177 | 178 | false 179 | 180 | 181 | true 182 | 183 | 184 | QAbstractItemView::InternalMove 185 | 186 | 187 | QAbstractItemView::NoSelection 188 | 189 | 190 | QAbstractItemView::ScrollPerPixel 191 | 192 | 193 | true 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | Advanced 208 | 209 | 210 | 211 | 212 | 213 | Enable the Tools Menu shortcut for these options. 214 | (Can also be accessed via Tools>Add-ons>Study Time Stats>Config) 215 | 216 | 217 | Show options shortcut in the Tools Menu 218 | 219 | 220 | true 221 | 222 | 223 | 224 | 225 | 226 | 227 | Include review/time stats for cards that've been deleted. 228 | 229 | 230 | Include reviews from deleted cards 231 | 232 | 233 | true 234 | 235 | 236 | 237 | 238 | 239 | 240 | Use Anki's rollover (next-day) hour when considering the time days are cut off. 241 | 242 | 243 | Use the next-day hour when calculating ranged times 244 | 245 | 246 | true 247 | 248 | 249 | 250 | 251 | 252 | 253 | Shows an "hour.min" format for all time outputs. 254 | (Otherwise uses "hh:mm") 255 | 256 | 257 | Use decimal format time outputs 258 | 259 | 260 | true 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 0 269 | 100 270 | 271 | 272 | 273 | Enabled Decks 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | Qt::TabFocus 282 | 283 | 284 | Enable stats for the selected deck(s). 285 | 286 | 287 | Enable 288 | 289 | 290 | 291 | 292 | 293 | 294 | Qt::TabFocus 295 | 296 | 297 | Disable stats for the selected deck(s). 298 | 299 | 300 | Disable 301 | 302 | 303 | 304 | 305 | 306 | 307 | Qt::Horizontal 308 | 309 | 310 | 311 | 40 312 | 20 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | QAbstractItemView::ExtendedSelection 323 | 324 | 325 | QAbstractItemView::SelectRows 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | About 337 | 338 | 339 | 340 | QLayout::SetNoConstraint 341 | 342 | 343 | 344 | 345 | 346 | 0 347 | 0 348 | 349 | 350 | 351 | QAbstractScrollArea::AdjustToContents 352 | 353 | 354 | true 355 | 356 | 357 | 358 | 359 | 0 360 | 0 361 | 456 362 | 555 363 | 364 | 365 | 366 | 367 | 0 368 | 0 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 0 377 | 0 378 | 379 | 380 | 381 | ## <p align="center">Study Time Stats<img src="{img_path}"></p> 382 | Adds a total and ranged study time statistic to Anki's main window. 383 | 384 | Version: {version} 385 | Have any issues or feedback? Feel free to post on the project's issue section on [GitHub](https://github.com/iamjustkoi/StudyTimeStats/issues)! 386 | 387 | [Releases/Changelog](https://github.com/iamjustkoi/StudyTimeStats/releases) 388 | [Source Code](https://github.com/iamjustkoi/StudyTimeStats) 389 | 390 | If you like the add-on and want to consider supporting me in anyway: 391 | 392 | 393 | Qt::AutoText 394 | 395 | 396 | Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop 397 | 398 | 399 | true 400 | 401 | 402 | true 403 | 404 | 405 | Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 0 414 | 0 415 | 416 | 417 | 418 | 419 | 6 420 | 421 | 422 | 6 423 | 424 | 425 | 6 426 | 427 | 428 | 6 429 | 430 | 431 | 432 | 433 | 434 | 0 435 | 42 436 | 437 | 438 | 439 | 440 | 200 441 | 16777215 442 | 443 | 444 | 445 | PointingHandCursor 446 | 447 | 448 | Qt::NoFocus 449 | 450 | 451 | Qt::CustomContextMenu 452 | 453 | 454 | Leave a review over at AnkiWeb! 455 | 456 | 457 | AnkiWeb 458 | 459 | 460 | 461 | res/img/anki_like.pngres/img/anki_like.png 462 | 463 | 464 | 465 | 32 466 | 32 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 0 476 | 42 477 | 478 | 479 | 480 | 481 | 200 482 | 16777215 483 | 484 | 485 | 486 | PointingHandCursor 487 | 488 | 489 | Qt::NoFocus 490 | 491 | 492 | Qt::CustomContextMenu 493 | 494 | 495 | Follow/support me on Patreon! 496 | 497 | 498 | Patreon 499 | 500 | 501 | 502 | res/img/patreon.pngres/img/patreon.png 503 | 504 | 505 | 506 | 32 507 | 32 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 0 517 | 42 518 | 519 | 520 | 521 | 522 | 200 523 | 16777215 524 | 525 | 526 | 527 | PointingHandCursor 528 | 529 | 530 | Qt::NoFocus 531 | 532 | 533 | Qt::CustomContextMenu 534 | 535 | 536 | Buy me a coffee with Ko-Fi! 537 | 538 | 539 | Ko-Fi 540 | 541 | 542 | 543 | res/img/kofilogo_blue.PNGres/img/kofilogo_blue.PNG 544 | 545 | 546 | 547 | 32 548 | 32 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | 557 | 558 | 559 | 560 | 0 561 | 0 562 | 563 | 564 | 565 | Every bit helps and is greatly appreciated! <3 566 | 567 | ### Text Macros 568 | All output text can also be filtered to show some more customized information (e.g. "Past %range" to "Past Week"). These can be used multiple times and will update whenever Anki's main window reloads. 569 | 570 | `%%` - can be used to return a single % symbol and disable filtering for any macro text (e.g. `%%` -> %, `%%range` -> %range) 571 | 572 | *Small warning: as a general rule, the more stats used/the larger the range of the stat, the longer it might take to load them all (some caching is also done on the side, too though). 573 | 574 | <br></br> 575 | <br></br> 576 | Thanks for downloading and hope you enjoy! 577 | 578 | -koi 579 | 580 | 581 | <br></br> 582 | <br></br> 583 | MIT License 584 | 585 | ©2022-2023 JustKoi (iamjustkoi) 586 | 587 | 588 | Qt::AutoText 589 | 590 | 591 | Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop 592 | 593 | 594 | true 595 | 596 | 597 | true 598 | 599 | 600 | Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 0 617 | 618 | 619 | 0 620 | 621 | 622 | 0 623 | 624 | 625 | 0 626 | 627 | 628 | 629 | 630 | Qt::Horizontal 631 | 632 | 633 | QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::RestoreDefaults 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 0 642 | 0 643 | 644 | 645 | 646 | <3 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | HoverButton 658 | QPushButton 659 |
.forms.h
660 |
661 |
662 | 663 | 664 | 665 | confirm_button_box 666 | accepted() 667 | OptionsDialog 668 | accept() 669 | 670 | 671 | 248 672 | 254 673 | 674 | 675 | 157 676 | 274 677 | 678 | 679 | 680 | 681 | confirm_button_box 682 | rejected() 683 | OptionsDialog 684 | reject() 685 | 686 | 687 | 316 688 | 260 689 | 690 | 691 | 286 692 | 274 693 | 694 | 695 | 696 | 697 |
698 | -------------------------------------------------------------------------------- /src/config.py: -------------------------------------------------------------------------------- 1 | # MIT License: Copyright (c) 2022-2023 JustKoi (iamjustkoi) 2 | # Full license text available in the "LICENSE" file, packaged with the add-on. 3 | 4 | import re 5 | from typing import List 6 | 7 | from anki.buildinfo import version 8 | from aqt import AnkiQt 9 | 10 | from .consts import CELL_HTML_SHELL, CURRENT_VERSION, Config, Direction, Macro, Range, String 11 | 12 | try: 13 | # from anki.utils import pointVersion 14 | from anki.utils import pointVersion 15 | except ImportError: 16 | def pointVersion(): 17 | return int(version.replace('2.1.', '')) 18 | 19 | try: 20 | ANKI_VERSION = int(version.replace('2.1.', '')) 21 | except ValueError: 22 | ANKI_VERSION = pointVersion() 23 | 24 | 25 | class TimeStatsConfigManager: 26 | 27 | def __init__(self, mw: AnkiQt): 28 | """ 29 | Generic config manager for accessing and writing addon config values. 30 | 31 | :param mw: Anki window to retrieve addon and other data from 32 | """ 33 | super().__init__() 34 | self.mw = mw 35 | self._addon = self.mw.addonManager.addonFromModule(__name__) 36 | self._meta = self.mw.addonManager.addonMeta(self._addon) 37 | 38 | self.config = self._init_config() 39 | 40 | self.decks = self.mw.col.decks if self.mw.col is not None else None 41 | 42 | # Add a constant key to meta 43 | self._meta['config'] = self.config 44 | 45 | def write_config(self): 46 | """ 47 | Writes the config manager's current values to the addon meta file. 48 | """ 49 | self.mw.addonManager.writeAddonMeta(self._addon, self._meta) 50 | 51 | def write_config_val(self, key: str, val): 52 | """ 53 | Creates a shallow copy of the current config and writes a single value to the addon meta file 54 | using the shallow copy as a template. 55 | """ 56 | config = self._init_config(deep=False) 57 | config[key] = val 58 | meta = self.mw.addonManager.addonMeta(self._addon) 59 | meta['config'] = config 60 | self.mw.addonManager.writeAddonMeta(self._addon, meta) 61 | 62 | def _init_config(self, deep=True): 63 | meta = self._meta if deep else self.mw.addonManager.addonMeta(self._addon) 64 | config = meta.get('config', Config.DEFAULT_CONFIG.copy()) 65 | config = _reformat_conf(config) if deep else config 66 | for field in Config.DEFAULT_CONFIG: 67 | if field not in config: 68 | # load temp defaults 69 | config[field] = Config.DEFAULT_CONFIG[field] 70 | 71 | return config 72 | 73 | 74 | def _reformat_conf(config: dict): 75 | """ 76 | Handles the addon's config updates/patching pipeline. 77 | 78 | :param config: The current config for the addon. 79 | :return: An updated addon config with updated formatting/patched changes. 80 | """ 81 | addon_ver = config.get(Config.VERSION, '0.0.0') 82 | formatted_addon_ver = re.sub('-.*', '', addon_ver) 83 | ver_numbers: List[int] = [int(n) for n in formatted_addon_ver.split('.')] 84 | 85 | # v1.3.5 86 | # Replaces "%from_custom_date" with the updated, custom-date-range hours macro. 87 | if ver_numbers[0] <= 1 and ver_numbers[1] <= 3 and ver_numbers[2] <= 5: 88 | def replace_macro(outer_conf: dict): 89 | for field in outer_conf: 90 | data = outer_conf[field] 91 | if isinstance(data, str) and re.match(fr'.*(? 2 | # Full license text available in the "LICENSE" file, packaged with the add-on. 3 | 4 | from aqt.qt import QT_VERSION_STR 5 | from aqt.theme import theme_manager 6 | 7 | CURRENT_VERSION = '2.1.4' 8 | 9 | ANKI_LEGACY_VER = 35 10 | ANKI_DEFAULT_ROLLOVER = 4 11 | ANKI_QT_VER = int(QT_VERSION_STR.split('.')[0]) 12 | 13 | UNIQUE_DATE = '2006-10-05' 14 | 15 | ADDON_ICON_PATH = '../res/img/stats_icon.svg' 16 | HEART_ICON_PATH = '../res/img/heart_icon.svg' 17 | KOFI_ICON_PATH = '../res/img/kofilogo_blue.PNG' 18 | PATREON_ICON_PATH = '../res/img/patreon.png' 19 | ANKI_LIKE_ICON_PATH = '../res/img/anki_like.png' 20 | 21 | REMOVE_ICON_PATH = '../res/img/remove_icon.svg' 22 | ADD_ICON_PATH = '../res/img/add_icon.svg' 23 | CODE_ICON_PATH = '../res/img/code_icon.svg' 24 | CHEVRON_ICON_PATH = '../res/img/chevron_down.svg' 25 | VERT_LINES_PATH = '../res/img/vert_lines.svg' 26 | HORIZ_LINES_PATH = '../res/img/horiz_lines.svg' 27 | HANDLES_PATH = '../res/img/vert_grip.svg' 28 | 29 | PATREON_URL = 'https://www.patreon.com/iamjustkoi' 30 | KOFI_URL = 'https://ko-fi.com/iamjustkoi' 31 | ANKI_URL = 'https://ankiweb.net/shared/info/1247171202' 32 | 33 | TABLE_ID = 'sts-table' 34 | COL_CLASS = 'sts-col' 35 | HORIZ_CELL_CLASS = 'flow-horizontal' 36 | VERT_CELL_CLASS = 'flow-vertical' 37 | HTML_SHELL = ''' 38 | 66 |
67 |
68 | {cell_data} 69 |
70 |
71 | ''' 72 | 73 | CELL_HTML_SHELL = '''
74 |
{{Title}}
75 |
{{Output}}
76 |
77 | ''' 78 | 79 | SUSPENDED = 'suspended' 80 | LEARN = 'learn' 81 | RELEARN = 'relearn' 82 | REVIEW = 'review' 83 | NEW = 'new' 84 | SIB_BURIED = 'sib_buried' 85 | MAN_BURIED = 'man_buried' 86 | BURIED = 'buried' 87 | 88 | 89 | class Macro: 90 | # Text 91 | CMD_RANGE = '%range' 92 | CMD_DATE = '%from_date' 93 | CMD_YEAR = '%from_year' 94 | CMD_FULL_DAY = '%from_full_day' 95 | CMD_DAY = '%from_day' 96 | CMD_DAYS = '%days' 97 | CMD_DATE_FORMATTED = r'%from_date:strf\{".*\"}' 98 | 99 | CMD_MONTH = '%from_month' 100 | CMD_FULL_MONTH = '%from_full_month' 101 | 102 | # Time 103 | CMD_TOTAL_HOURS = '%total_hrs' 104 | 105 | CMD_RANGE_HOURS = '%range_hrs' 106 | CMD_DAY_HOURS = '%day_hrs' 107 | CMD_WEEK_HOURS = '%week_hrs' 108 | CMD_TWO_WEEKS_HOURS = '%two_week_hrs' 109 | CMD_MONTH_HOURS = '%month_hrs' 110 | CMD_YEAR_HOURS = '%year_hrs' 111 | 112 | CMD_ETA_HOURS = '%eta_hrs' 113 | 114 | CMD_HIGHEST_DAY_HOURS = '%highest_day_hrs' 115 | CMD_HIGHEST_WEEK_HOURS = '%highest_week_hrs' 116 | CMD_HIGHEST_MONTH_HOURS = '%highest_month_hrs' 117 | CMD_HIGHEST_YEAR_HOURS = '%highest_year_hrs' 118 | 119 | # Avg 120 | CMD_CARD_AVERAGE_HOURS = '%card_avg_hrs' 121 | CMD_CARD_AVERAGE_REVIEWS = '%card_avg_rev' 122 | CMD_DAY_AVERAGE_HOURS = '%day_avg_hrs' 123 | CMD_DAY_AVERAGE_REVIEWS = '%day_avg_rev' 124 | 125 | # Previous Time 126 | CMD_PREVIOUS_RANGE_HOURS = '%prev_range_hrs' 127 | CMD_PREVIOUS_DAY_HOURS = '%prev_day_hrs' 128 | CMD_PREVIOUS_WEEK_HOURS = '%prev_week_hrs' 129 | CMD_PREVIOUS_TWO_WEEKS_HOURS = '%prev_two_week_hrs' 130 | CMD_PREVIOUS_MONTH_HOURS = '%prev_month_hrs' 131 | CMD_PREVIOUS_YEAR_HOURS = '%prev_year_hrs' 132 | CMD_FROM_DATE_HOURS = '%from_custom_hrs' 133 | 134 | # Reviews 135 | CMD_TOTAL_REVIEWS = '%total_rev' 136 | CMD_RANGE_REVIEWS = '%range_rev' 137 | CMD_DAY_REVIEWS = '%day_rev' 138 | CMD_WEEK_REVIEWS = '%week_rev' 139 | CMD_TWO_WEEKS_REVIEWS = '%two_week_rev' 140 | CMD_MONTH_REVIEWS = '%month_rev' 141 | CMD_YEAR_REVIEWS = '%year_rev' 142 | 143 | CMD_PREVIOUS_RANGE_REVIEWS = '%prev_range_rev' 144 | CMD_PREVIOUS_DAY_REVIEWS = '%prev_day_rev' 145 | CMD_PREVIOUS_WEEK_REVIEWS = '%prev_week_rev' 146 | CMD_PREVIOUS_TWO_WEEKS_REVIEWS = '%prev_two_week_rev' 147 | CMD_PREVIOUS_MONTH_REVIEWS = '%prev_month_rev' 148 | CMD_PREVIOUS_YEAR_REVIEWS = '%prev_year_rev' 149 | CMD_FROM_DATE_REVIEWS = '%from_custom_rev' 150 | 151 | CMD_HIGHEST_DAY_REVIEWS = '%highest_day_rev' 152 | CMD_HIGHEST_WEEK_REVIEWS = '%highest_week_rev' 153 | CMD_HIGHEST_MONTH_REVIEWS = '%highest_month_rev' 154 | CMD_HIGHEST_YEAR_REVIEWS = '%highest_year_rev' 155 | 156 | # Misc 157 | CMD_EVAL = '%eval{' 158 | 159 | CMD_PRECISION = r':p' 160 | PRECISION_EXTRA = r'\d*' 161 | 162 | CMD_STATE = r':state' 163 | STATE_EXTRA = r'[\w,]*' 164 | 165 | # Definitions for all macros 166 | DEFINITIONS = { 167 | # Text 168 | CMD_RANGE: 169 | '''label for the currently selected range''', 170 | CMD_DATE: 171 | '''current range's start date using the system's locale''', 172 | CMD_YEAR: 173 | '''current range's year''', 174 | CMD_FULL_DAY: 175 | '''range filter's full start day''', 176 | CMD_DAY: 177 | '''range filter's starting day using a compact format''', 178 | CMD_DAYS: 179 | '''total days the range filter checks against''', 180 | CMD_DATE_FORMATTED: 181 | '''formatted date string using the current range's from-date (e.g. %from_date:strf{"%x"} -> 2022-01-01)''', 182 | CMD_MONTH: 183 | '''range filter's month name using a compact format''', 184 | CMD_FULL_MONTH: 185 | '''range filter's full month name''', 186 | 187 | # Time 188 | CMD_TOTAL_HOURS: 189 | '''total study time''', 190 | CMD_RANGE_HOURS: 191 | '''total study time for the current range''', 192 | 193 | CMD_DAY_HOURS: 194 | '''total study time for the past day''', 195 | CMD_WEEK_HOURS: 196 | '''total study time for the past week''', 197 | CMD_TWO_WEEKS_HOURS: 198 | '''total study time for the past two weeks''', 199 | CMD_MONTH_HOURS: 200 | '''total study time for the past month''', 201 | CMD_YEAR_HOURS: 202 | '''total study time for the past year''', 203 | 204 | CMD_ETA_HOURS: 205 | '''estimated study time for the current card queue''', 206 | 207 | CMD_HIGHEST_DAY_HOURS: 208 | '''highest total study time for all days in the current range''', 209 | CMD_HIGHEST_WEEK_HOURS: 210 | '''highest total study time for all weeks in the current range''', 211 | CMD_HIGHEST_MONTH_HOURS: 212 | '''highest total study time for all months in the current range''', 213 | CMD_HIGHEST_YEAR_HOURS: 214 | '''highest total study time for all years in the current range''', 215 | 216 | # Avg 217 | CMD_CARD_AVERAGE_HOURS: 218 | '''average study time per card for the current range''', 219 | CMD_CARD_AVERAGE_REVIEWS: 220 | '''average reviews per card for the current range''', 221 | CMD_DAY_AVERAGE_HOURS: 222 | '''average study time per day for the current range''', 223 | CMD_DAY_AVERAGE_REVIEWS: 224 | '''average reviews per day for the current range''', 225 | 226 | # Previous Time 227 | CMD_PREVIOUS_RANGE_HOURS: 228 | '''total study time for the previous range''', 229 | CMD_PREVIOUS_DAY_HOURS: 230 | '''total study time for the previous day''', 231 | CMD_PREVIOUS_WEEK_HOURS: 232 | '''total study time for the previous week''', 233 | CMD_PREVIOUS_TWO_WEEKS_HOURS: 234 | '''total study time for the previous two weeks''', 235 | CMD_PREVIOUS_MONTH_HOURS: 236 | '''total study time for the previous month''', 237 | CMD_PREVIOUS_YEAR_HOURS: 238 | '''total study time for the previous year''', 239 | 240 | CMD_FROM_DATE_HOURS: 241 | '''total study time for a custom date range (%from_custom_hrs:: (YYYY-MM-DD))''', 242 | 243 | # Reviews 244 | CMD_TOTAL_REVIEWS: 245 | '''total reviews''', 246 | CMD_RANGE_REVIEWS: 247 | '''total reviews for the current range''', 248 | 249 | CMD_DAY_REVIEWS: 250 | '''total reviews for the past day''', 251 | CMD_WEEK_REVIEWS: 252 | '''total reviews for the past week''', 253 | CMD_TWO_WEEKS_REVIEWS: 254 | '''total reviews for the past two weeks''', 255 | CMD_MONTH_REVIEWS: 256 | '''total reviews for the past month''', 257 | CMD_YEAR_REVIEWS: 258 | '''total reviews for the past year''', 259 | 260 | CMD_PREVIOUS_RANGE_REVIEWS: 261 | '''total reviews for the previous range''', 262 | CMD_PREVIOUS_DAY_REVIEWS: 263 | '''total reviews for the previous day''', 264 | CMD_PREVIOUS_WEEK_REVIEWS: 265 | '''total reviews for the previous week''', 266 | CMD_PREVIOUS_TWO_WEEKS_REVIEWS: 267 | '''total reviews for the previous two weeks''', 268 | CMD_PREVIOUS_MONTH_REVIEWS: 269 | '''total reviews for the previous month''', 270 | CMD_PREVIOUS_YEAR_REVIEWS: 271 | '''total reviews for the previous year''', 272 | 273 | CMD_FROM_DATE_REVIEWS: 274 | '''total reviews for a custom date range (%from_custom_hrs:: (YYYY-MM-DD))''', 275 | 276 | CMD_HIGHEST_DAY_REVIEWS: 277 | '''highest total reviews for all days in the current range''', 278 | CMD_HIGHEST_WEEK_REVIEWS: 279 | '''highest total reviews for all weeks in the current range''', 280 | CMD_HIGHEST_MONTH_REVIEWS: 281 | '''highest total reviews for all months in the current range''', 282 | CMD_HIGHEST_YEAR_REVIEWS: 283 | '''highest total reviews for all years in the current range''', 284 | 285 | # Misc 286 | CMD_EVAL: 287 | '''customized output that accepts any macros, math, or python as input (warning: unsafe implementation)''', 288 | 289 | CMD_PRECISION: 290 | '''filters an output's precision for time-based, decimal macros (%range_hrs:p{<0-4>})''', 291 | 292 | CMD_STATE: 293 | '''filters an output to the current state of a card, separated by commas''' 294 | f''' (%range_hrs:state{{<{NEW}/{LEARN}/{REVIEW}/{SUSPENDED}/{BURIED}>}},''' 295 | f''' e.g. %range_hrs:state{{{LEARN},{REVIEW}}})''', 296 | } 297 | 298 | 299 | class String: 300 | TOTAL = 'Total' 301 | PAST_RANGE = 'Past %range' 302 | HRS = 'hrs' 303 | MIN = 'min' 304 | OPTIONS_ACTION = 'Study &Time Stats Options...' 305 | USE_CALENDAR = 'Use Calendar' 306 | DAYS = 'Days' 307 | WEEK = 'Week' 308 | TWO_WEEKS = '2 Weeks' 309 | MONTH = 'Month' 310 | YEAR = 'Year' 311 | COPY_LINK_ACTION = 'Copy &Link Location' 312 | ENABLE_ACTION = '&Enable' 313 | DISABLE_ACTION = '&Disable' 314 | INSERT = 'Insert' 315 | ERR = 'ERR' 316 | DELETE_CELL = 'Delete view?' 317 | 318 | 319 | class Range: 320 | TOTAL, WEEK, TWO_WEEKS, MONTH, YEAR, CUSTOM = -1, 0, 1, 2, 3, 4 321 | DAYS_IN = {WEEK: 7, TWO_WEEKS: 14, MONTH: 30, YEAR: 365, CUSTOM: 1} 322 | LABEL = {WEEK: String.WEEK, TWO_WEEKS: String.TWO_WEEKS, MONTH: String.MONTH, YEAR: String.YEAR} 323 | 324 | 325 | class Weekday: 326 | SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY = 0, 1, 2, 3, 4, 5, 6 327 | 328 | 329 | class Direction: 330 | VERTICAL = 'vertical' 331 | HORIZONTAL = 'horizontal' 332 | 333 | 334 | class Color: 335 | # [Light, Dark] 336 | HOVER = ['#040404', '#b0b0b0'] 337 | BUTTON_ICON = ['#808080', '#8a8a8a'] 338 | TITLE_DEFAULT = ['#000000', '#FFFFFF'] 339 | OUTPUT_DEFAULT = ['#76bfb4', '#76bfb4'] 340 | BUTTON_ACTIVE = ['#cacaca', '#5b5b5b'] 341 | 342 | 343 | class Config: 344 | TOOLBAR_ENABLED = 'Use_Toolbar_Options' 345 | CUSTOM_DAYS = 'Custom_Days' 346 | CUSTOM_TOTAL_TEXT = 'Custom_Total_Text' 347 | CUSTOM_RANGE_TEXT = 'Custom_Range_Text' 348 | CUSTOM_TOTAL_HOURS = 'Custom_Total_Hrs' 349 | CUSTOM_RANGE_HOURS = 'Custom_Range_Hrs' 350 | CUSTOM_HOURS_TEXT = 'Custom_Hrs_Text' 351 | CUSTOM_MIN_TEXT = 'Custom_Min_Text' 352 | BROWSER_ENABLED = 'Browser_Enabled' 353 | OVERVIEW_ENABLED = 'Overview_Enabled' 354 | CONGRATS_ENABLED = 'Congrats_Enabled' 355 | INCLUDE_DELETED = 'Include_Deleted_Reviews' 356 | USE_ROLLOVER = 'Use_Rollover_Hour' 357 | USE_DECIMAL = 'Use_Decimal_Format' 358 | EXCLUDED_DIDS = "Excluded_Deck_IDs" 359 | CELLS_DATA = "cellsData" 360 | WIN_SIZE = 'winSize' 361 | VERSION = 'version' 362 | 363 | TITLE = 'title' 364 | OUTPUT = 'output' 365 | TITLE_COLOR = 'titleColor' 366 | OUTPUT_COLOR = 'outputColor' 367 | DIRECTION = 'direction' 368 | RANGE = 'range' 369 | USE_CALENDAR = 'useCalendar' 370 | WEEK_START = 'weekStart' 371 | DAYS = 'days' 372 | HRS_UNIT = 'hrsUnit' 373 | MIN_UNIT = 'minUnit' 374 | HTML = 'html' 375 | DEFAULT_CELL_DATA = { 376 | TITLE: String.PAST_RANGE, 377 | OUTPUT: Macro.CMD_RANGE_HOURS, 378 | TITLE_COLOR: Color.TITLE_DEFAULT[theme_manager.get_night_mode()], 379 | OUTPUT_COLOR: Color.OUTPUT_DEFAULT[theme_manager.get_night_mode()], 380 | DIRECTION: Direction.VERTICAL, 381 | RANGE: Range.WEEK, 382 | USE_CALENDAR: True, 383 | WEEK_START: Weekday.SUNDAY, 384 | DAYS: 7, 385 | HRS_UNIT: String.HRS, 386 | MIN_UNIT: String.MIN, 387 | HTML: CELL_HTML_SHELL, 388 | } 389 | 390 | DEFAULT_CONFIG = { 391 | TOOLBAR_ENABLED: True, 392 | BROWSER_ENABLED: True, 393 | OVERVIEW_ENABLED: True, 394 | CONGRATS_ENABLED: True, 395 | INCLUDE_DELETED: True, 396 | USE_ROLLOVER: True, 397 | USE_DECIMAL: True, 398 | EXCLUDED_DIDS: [1], 399 | CELLS_DATA: [ 400 | { 401 | TITLE: String.TOTAL, 402 | OUTPUT: Macro.CMD_RANGE_HOURS, 403 | TITLE_COLOR: Color.TITLE_DEFAULT[theme_manager.get_night_mode()], 404 | OUTPUT_COLOR: Color.OUTPUT_DEFAULT[theme_manager.get_night_mode()], 405 | DIRECTION: Direction.VERTICAL, 406 | RANGE: Range.TOTAL, 407 | USE_CALENDAR: True, 408 | WEEK_START: Weekday.SUNDAY, 409 | DAYS: 7, 410 | HRS_UNIT: String.HRS, 411 | MIN_UNIT: String.MIN, 412 | HTML: CELL_HTML_SHELL, 413 | }, 414 | { 415 | TITLE: String.PAST_RANGE, 416 | OUTPUT: Macro.CMD_RANGE_HOURS, 417 | TITLE_COLOR: Color.TITLE_DEFAULT[theme_manager.get_night_mode()], 418 | OUTPUT_COLOR: Color.OUTPUT_DEFAULT[theme_manager.get_night_mode()], 419 | DIRECTION: Direction.VERTICAL, 420 | RANGE: Range.WEEK, 421 | USE_CALENDAR: True, 422 | WEEK_START: Weekday.SUNDAY, 423 | DAYS: 7, 424 | HRS_UNIT: String.HRS, 425 | MIN_UNIT: String.MIN, 426 | HTML: CELL_HTML_SHELL, 427 | } 428 | ] 429 | } 430 | -------------------------------------------------------------------------------- /src/toolbar.py: -------------------------------------------------------------------------------- 1 | # MIT License: Copyright (c) 2022-2023 JustKoi (iamjustkoi) 2 | # Full license text available in the "LICENSE" file, packaged with the add-on. 3 | 4 | from aqt import mw 5 | from aqt.qt import QAction 6 | 7 | from .config import TimeStatsConfigManager 8 | from .consts import ( 9 | Config, 10 | String, 11 | ) 12 | from .options import TimeStatsOptionsDialog 13 | 14 | 15 | def build_toolbar_actions(): 16 | refresh_tools_menu_action() 17 | mw.addonManager.setConfigAction(__name__, on_options_called) 18 | 19 | 20 | def refresh_tools_menu_action(__changes=None, __obj=None): 21 | """ 22 | Updates the toolbar actions menu with the options shortcut. Expects an Operation Change hook call, 23 | but can also be used as a general update push, too. 24 | 25 | :param __changes: unused OpChanges object 26 | :param __obj: unused options object 27 | """ 28 | if TimeStatsConfigManager(mw).config[Config.TOOLBAR_ENABLED]: 29 | options_action = QAction(String.OPTIONS_ACTION, mw) 30 | options_action.triggered.connect(on_options_called) 31 | # Handles edge cases where toolbar action already exists in the tools menu 32 | if options_action.text() not in [action.text() for action in mw.form.menuTools.actions()]: 33 | mw.form.menuTools.addAction(options_action) 34 | else: 35 | for action in mw.form.menuTools.actions(): 36 | if action.text() == String.OPTIONS_ACTION: 37 | mw.form.menuTools.removeAction(action) 38 | 39 | 40 | def on_options_called(): 41 | """ 42 | Initializes and opens the options dialog. 43 | """ 44 | dialog = TimeStatsOptionsDialog(TimeStatsConfigManager(mw)) 45 | dialog.exec() 46 | --------------------------------------------------------------------------------