def __init__(self, appPath): QWidget.__init__(self) global rc rc = ResourceCache(normpath(appPath + "/resources")) self.__config = Config(appPath) self.__noteProvider = SqliteNoteProvider( normpath(appPath + "/resources/notes/notes.db")) self.__remoteNoteProvider = None self.__notes = [] self.__tags = [] self.setWindowTitle("Scroll, Quill & INK " + version) self.setMinimumSize(400, 240) self.resize(800, 600) icon = QIcon() icon.addPixmap(rc.pixmap("sqink-16x16.png")) icon.addPixmap(rc.pixmap("sqink-32x32.png")) self.setWindowIcon(icon) layout = QVBoxLayout() layout.setContentsMargins(5, 5, 5, 2) self.setLayout(layout) splitter = QSplitter() splitter.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) # noinspection PyCallByClass,PyTypeChecker splitter.setStyle(QStyleFactory.create("Cleanlooks")) layout.addWidget(splitter) self.__statusBar = QStatusBar() self.__statusBar.setSizeGripEnabled(False) layout.addWidget(self.__statusBar) self.__lblNotesCount = QLabel() self.__lblNotesCount.setStyleSheet("border:1px solid #b2dfdb; border-radius:0.5em; background-color:#b2dfdb;" +\ " padding:0 0.5em;") self.__statusBar.addPermanentWidget(self.__lblNotesCount) self.__lblError = QLabel() self.__lblError.setStyleSheet( "border-radius:0.5em; background-color:#f36c60; padding:0 0.5em;") self.__lblError.setVisible(False) self.__statusBar.addWidget(self.__lblError) panelA = QWidget() splitter.addWidget(panelA) layoutA = QGridLayout() layoutA.setContentsMargins(0, 0, 0, 0) panelA.setLayout(layoutA) barButtons = QWidget() layoutButtons = QHBoxLayout() layoutButtons.setContentsMargins(0, 0, 0, 0) barButtons.setLayout(layoutButtons) layoutA.addWidget(barButtons, 0, 0, 1, 2) btnNew = QPushButton() btnNew.setIcon(rc.icon("note-new-16x16.png")) btnNew.setToolTip("New note (CTRL + N)") # noinspection PyUnresolvedReferences btnNew.clicked.connect(self.__onNewNoteClicked) btnNew.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_N)) layoutButtons.addWidget(btnNew) self.__btnEdit = QPushButton() self.__btnEdit.setIcon(rc.icon("note-edit-16x16.png")) self.__btnEdit.setToolTip("Edit note (CTRL + E)") # noinspection PyUnresolvedReferences self.__btnEdit.clicked.connect(self.__onEditNoteClicked) self.__btnEdit.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_E)) layoutButtons.addWidget(self.__btnEdit) self.__btnDelete = QPushButton() self.__btnDelete.setIcon(rc.icon("note-delete-16x16.png")) self.__btnDelete.setToolTip("Delete note (CTRL + L)") # noinspection PyUnresolvedReferences self.__btnDelete.clicked.connect(self.__onDeleteNoteClicked) self.__btnDelete.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_L)) layoutButtons.addWidget(self.__btnDelete) layoutButtons.addStretch(1) self.__btnSync = QPushButton() self.__btnSync.setIcon(rc.icon("sync-16x16.png")) self.__btnSync.setToolTip("Sync (CTRL + I)") # noinspection PyUnresolvedReferences self.__btnSync.clicked.connect(self.__onSyncClicked) self.__btnSync.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_I)) layoutButtons.addWidget(self.__btnSync) btnConfig = QPushButton() btnConfig.setIcon(rc.icon("preferences-16x16.png")) btnConfig.setToolTip("Preferences") # noinspection PyUnresolvedReferences btnConfig.clicked.connect(self.__onPreferencesClicked) layoutButtons.addWidget(btnConfig) lblSearch = QLabel() lblSearch.setPixmap(rc.pixmap("search-16x16.png")) lblSearch.setToolTip("Find (CTRL + F)") layoutA.addWidget(lblSearch, 1, 0) self.__txtSearch = QLineEdit() self.__txtSearch.setValidator( QRegExpValidator( QRegExp("[A-ZÁÉÍÓÚÜÑa-záéíóúüñ0-9 @:/#&=\.\-\?\\\\]+"))) # noinspection PyUnresolvedReferences self.__txtSearch.textEdited.connect(self.__onSearchEdited) QShortcut(QKeySequence(Qt.CTRL + Qt.Key_F), self.__txtSearch, self.__txtSearch.setFocus) layoutA.addWidget(self.__txtSearch, 1, 1) lblTag = QLabel() lblTag.setPixmap(rc.pixmap("tag-16x16.png")) lblTag.setToolTip("Filter by tag (CTRL + T)") layoutA.addWidget(lblTag, 2, 0) self.__cmbTag = QComboBox() # noinspection PyUnresolvedReferences self.__cmbTag.activated.connect(self.__onTagFilterSelectionChanged) QShortcut(QKeySequence(Qt.CTRL + Qt.Key_T), self.__cmbTag, self.__onTagFilterShortcutPressed) layoutA.addWidget(self.__cmbTag, 2, 1) self.__lstNotes = ListWidget(self.__onDeleteNoteClicked) self.__lstNotes.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.__lstNotes.setItemDelegate(ListItemDelegate()) # noinspection PyUnresolvedReferences self.__lstNotes.itemSelectionChanged.connect( self.__onNoteSelectionChanged) layoutA.addWidget(self.__lstNotes, 3, 0, 1, 2) self.__viewerEditor = ViewerEditor(self) splitter.addWidget(self.__viewerEditor) self.configureSync() self.reload() self.__txtSearch.setFocus()
def __init__(self, appPath): QWidget.__init__(self) rc.init(normpath(appPath + "/resources")) self.__config= Config(appPath) self.__noteProvider= SqliteNoteProvider(normpath(appPath + "/resources/notes/notes.db")) self.setWindowTitle("Scroll, Quill & INK") self.setMinimumSize(400, 240) self.resize(800, 600) icon= QIcon() icon.addPixmap(rc.pixmap("sqink-16x16.png")) icon.addPixmap(rc.pixmap("sqink-32x32.png")) self.setWindowIcon(icon) layout= QVBoxLayout() layout.setContentsMargins(5, 5, 5, 2) self.setLayout(layout) splitter= QSplitter() splitter.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) splitter.setStyle(QStyleFactory.create("Cleanlooks")) layout.addWidget(splitter) self.__statusBar= QStatusBar() self.__statusBar.setSizeGripEnabled(False) layout.addWidget(self.__statusBar) self.__lblNotesCount= QLabel() self.__lblNotesCount.setStyleSheet("border-radius:0.5em; background-color:#b2dfdb; padding:0 0.5em;") self.__statusBar.addPermanentWidget(self.__lblNotesCount) self.__lblError= QLabel() self.__lblError.setStyleSheet("border-radius:0.5em; background-color:#f36c60; padding:0 0.5em;") self.__lblError.setVisible(False) self.__statusBar.addWidget(self.__lblError) panelA= QWidget() splitter.addWidget(panelA) layoutA= QGridLayout() layoutA.setContentsMargins(0, 0, 0, 0) panelA.setLayout(layoutA) barButtons= QWidget() layoutButtons= QHBoxLayout() layoutButtons.setContentsMargins(0, 0, 0, 0) barButtons.setLayout(layoutButtons) layoutA.addWidget(barButtons, 0, 0, 1, 2) btnNew= QPushButton() btnNew.setIcon(rc.icon("note-new-16x16.png")) btnNew.setToolTip("New note (CTRL + N)") btnNew.clicked.connect(self.__onNewNoteClicked) btnNew.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_N)) layoutButtons.addWidget(btnNew) self.__btnEdit= QPushButton() self.__btnEdit.setIcon(rc.icon("note-edit-16x16.png")) self.__btnEdit.setToolTip("Edit note (CTRL + E)") self.__btnEdit.clicked.connect(self.__onEditNoteClicked) self.__btnEdit.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_E)) layoutButtons.addWidget(self.__btnEdit) self.__btnDelete= QPushButton() self.__btnDelete.setIcon(rc.icon("note-delete-16x16.png")) self.__btnDelete.setToolTip("Delete note (CTRL + L)") self.__btnDelete.clicked.connect(self.__onDeleteNoteClicked) self.__btnDelete.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_L)) layoutButtons.addWidget(self.__btnDelete) layoutButtons.addStretch(1) self.__btnSync= QPushButton() self.__btnSync.setIcon(rc.icon("sync-16x16.png")) self.__btnSync.setToolTip("Sync (CTRL + I)") self.__btnSync.clicked.connect(self.__onSyncClicked) self.__btnSync.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_I)) layoutButtons.addWidget(self.__btnSync) btnConfig= QPushButton() btnConfig.setIcon(rc.icon("preferences-16x16.png")) btnConfig.setToolTip("Preferences") btnConfig.clicked.connect(self.__onPreferencesClicked) layoutButtons.addWidget(btnConfig) lblSearch= QLabel() lblSearch.setPixmap(rc.pixmap("search-16x16.png")) lblSearch.setToolTip("Find (CTRL + F)") layoutA.addWidget(lblSearch, 1, 0) self.__txtSearch= QLineEdit() self.__txtSearch.setValidator(QRegExpValidator(QRegExp("[A-ZÁÉÍÓÚÜÑa-záéíóúüñ0-9 @:/#&=\.\-\?\\\\]+"))) self.__txtSearch.textEdited.connect(self.__onSearchEdited) QShortcut(QKeySequence(Qt.CTRL + Qt.Key_F), self.__txtSearch, self.__txtSearch.setFocus) layoutA.addWidget(self.__txtSearch, 1, 1) lblTag= QLabel() lblTag.setPixmap(rc.pixmap("tag-16x16.png")) lblTag.setToolTip("Filter by tag (CTRL + T)") layoutA.addWidget(lblTag, 2, 0) self.__cmbTag= QComboBox() self.__cmbTag.activated.connect(self.__onTagFilterSelectionChanged) QShortcut(QKeySequence(Qt.CTRL + Qt.Key_T), self.__cmbTag, self.__onTagFilterShortcutPressed) layoutA.addWidget(self.__cmbTag, 2, 1) self.__lstNotes= ListWidget(self.__onDeleteNoteClicked) self.__lstNotes.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.__lstNotes.setItemDelegate(ListItemDelegate()) self.__lstNotes.itemSelectionChanged.connect(self.__onNoteSelectionChanged) layoutA.addWidget(self.__lstNotes, 3, 0, 1, 2) self.__viewerEditor= ViewerEditor(self) splitter.addWidget(self.__viewerEditor) self.configureSync() self.reload() self.__txtSearch.setFocus()
class Window(QWidget): tagsChanged = Signal(list) def __init__(self, appPath): QWidget.__init__(self) global rc rc = ResourceCache(normpath(appPath + "/resources")) self.__config = Config(appPath) self.__noteProvider = SqliteNoteProvider( normpath(appPath + "/resources/notes/notes.db")) self.__remoteNoteProvider = None self.__notes = [] self.__tags = [] self.setWindowTitle("Scroll, Quill & INK " + version) self.setMinimumSize(400, 240) self.resize(800, 600) icon = QIcon() icon.addPixmap(rc.pixmap("sqink-16x16.png")) icon.addPixmap(rc.pixmap("sqink-32x32.png")) self.setWindowIcon(icon) layout = QVBoxLayout() layout.setContentsMargins(5, 5, 5, 2) self.setLayout(layout) splitter = QSplitter() splitter.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) # noinspection PyCallByClass,PyTypeChecker splitter.setStyle(QStyleFactory.create("Cleanlooks")) layout.addWidget(splitter) self.__statusBar = QStatusBar() self.__statusBar.setSizeGripEnabled(False) layout.addWidget(self.__statusBar) self.__lblNotesCount = QLabel() self.__lblNotesCount.setStyleSheet("border:1px solid #b2dfdb; border-radius:0.5em; background-color:#b2dfdb;" +\ " padding:0 0.5em;") self.__statusBar.addPermanentWidget(self.__lblNotesCount) self.__lblError = QLabel() self.__lblError.setStyleSheet( "border-radius:0.5em; background-color:#f36c60; padding:0 0.5em;") self.__lblError.setVisible(False) self.__statusBar.addWidget(self.__lblError) panelA = QWidget() splitter.addWidget(panelA) layoutA = QGridLayout() layoutA.setContentsMargins(0, 0, 0, 0) panelA.setLayout(layoutA) barButtons = QWidget() layoutButtons = QHBoxLayout() layoutButtons.setContentsMargins(0, 0, 0, 0) barButtons.setLayout(layoutButtons) layoutA.addWidget(barButtons, 0, 0, 1, 2) btnNew = QPushButton() btnNew.setIcon(rc.icon("note-new-16x16.png")) btnNew.setToolTip("New note (CTRL + N)") # noinspection PyUnresolvedReferences btnNew.clicked.connect(self.__onNewNoteClicked) btnNew.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_N)) layoutButtons.addWidget(btnNew) self.__btnEdit = QPushButton() self.__btnEdit.setIcon(rc.icon("note-edit-16x16.png")) self.__btnEdit.setToolTip("Edit note (CTRL + E)") # noinspection PyUnresolvedReferences self.__btnEdit.clicked.connect(self.__onEditNoteClicked) self.__btnEdit.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_E)) layoutButtons.addWidget(self.__btnEdit) self.__btnDelete = QPushButton() self.__btnDelete.setIcon(rc.icon("note-delete-16x16.png")) self.__btnDelete.setToolTip("Delete note (CTRL + L)") # noinspection PyUnresolvedReferences self.__btnDelete.clicked.connect(self.__onDeleteNoteClicked) self.__btnDelete.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_L)) layoutButtons.addWidget(self.__btnDelete) layoutButtons.addStretch(1) self.__btnSync = QPushButton() self.__btnSync.setIcon(rc.icon("sync-16x16.png")) self.__btnSync.setToolTip("Sync (CTRL + I)") # noinspection PyUnresolvedReferences self.__btnSync.clicked.connect(self.__onSyncClicked) self.__btnSync.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_I)) layoutButtons.addWidget(self.__btnSync) btnConfig = QPushButton() btnConfig.setIcon(rc.icon("preferences-16x16.png")) btnConfig.setToolTip("Preferences") # noinspection PyUnresolvedReferences btnConfig.clicked.connect(self.__onPreferencesClicked) layoutButtons.addWidget(btnConfig) lblSearch = QLabel() lblSearch.setPixmap(rc.pixmap("search-16x16.png")) lblSearch.setToolTip("Find (CTRL + F)") layoutA.addWidget(lblSearch, 1, 0) self.__txtSearch = QLineEdit() self.__txtSearch.setValidator( QRegExpValidator( QRegExp("[A-ZÁÉÍÓÚÜÑa-záéíóúüñ0-9 @:/#&=\.\-\?\\\\]+"))) # noinspection PyUnresolvedReferences self.__txtSearch.textEdited.connect(self.__onSearchEdited) QShortcut(QKeySequence(Qt.CTRL + Qt.Key_F), self.__txtSearch, self.__txtSearch.setFocus) layoutA.addWidget(self.__txtSearch, 1, 1) lblTag = QLabel() lblTag.setPixmap(rc.pixmap("tag-16x16.png")) lblTag.setToolTip("Filter by tag (CTRL + T)") layoutA.addWidget(lblTag, 2, 0) self.__cmbTag = QComboBox() # noinspection PyUnresolvedReferences self.__cmbTag.activated.connect(self.__onTagFilterSelectionChanged) QShortcut(QKeySequence(Qt.CTRL + Qt.Key_T), self.__cmbTag, self.__onTagFilterShortcutPressed) layoutA.addWidget(self.__cmbTag, 2, 1) self.__lstNotes = ListWidget(self.__onDeleteNoteClicked) self.__lstNotes.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.__lstNotes.setItemDelegate(ListItemDelegate()) # noinspection PyUnresolvedReferences self.__lstNotes.itemSelectionChanged.connect( self.__onNoteSelectionChanged) layoutA.addWidget(self.__lstNotes, 3, 0, 1, 2) self.__viewerEditor = ViewerEditor(self) splitter.addWidget(self.__viewerEditor) self.configureSync() self.reload() self.__txtSearch.setFocus() def closeEvent(self, event): self.__noteProvider.close() QWidget.closeEvent(self, event) def config(self): return self.__config def noteProvider(self): return self.__noteProvider def remoteNoteProvider(self): return self.__remoteNoteProvider def textFilter(self): return self.__txtSearch.text().strip() def configureSync(self): config = self.config() if config.isDropboxEnabled(): self.__remoteNoteProvider = DropboxNoteProvider( config.getDropboxToken(), config.getDropboxFolder(), config.getProxyHost(), config.getProxyPort(), config.getProxyUser(), config.getProxyPassword()) self.__btnSync.setDisabled(False) elif config.isGoogleDriveEnabled(): self.__remoteNoteProvider = GoogleDriveNoteProvider( config.getGoogleDriveToken(), config.getProxyHost(), config.getProxyPort(), config.getProxyUser(), config.getProxyPassword()) self.__btnSync.setDisabled(False) else: self.__remoteNoteProvider = None self.__btnSync.setDisabled(True) def disableSync(self): config = self.config() if config.isDropboxEnabled(): config.setDropboxEnabled(False) config.setDropboxToken(None) else: config.setGoogleDriveEnabled(False) config.setGoogleDriveToken(None) config.save() self.configureSync() def updateProxy(self): self.__viewerEditor.updateProxy() def showStatus(self, message, seconds=5, immediate=False): self.clearError() self.__statusBar.showMessage(message, timeout=seconds * 1000) if immediate: self.__statusBar.repaint() def showError(self, message): self.__statusBar.clearMessage() self.__lblError.setText(message) self.__lblError.setVisible(True) def clearError(self): self.__lblError.setVisible(False) def reload(self, selectNoteOnViewerEditor=False, skipViewerEditor=False): self.__notes = sorted(self.noteProvider().list(), key=lambda n: n.title.lower()) self.__tags = listTags(self.__notes) self.__refreshTagCombo() self.__refreshNoteList(selectNoteOnViewerEditor) self.__lblNotesCount.setText("%d notes" % len(self.__notes)) if not skipViewerEditor: self.__refreshNoteViewerEditor() self.__refreshNoteButtons() # noinspection PyUnresolvedReferences self.tagsChanged.emit(self.__tags) def __setSelectedItem(self, uuid=None): self.__lstNotes.setDisabled(True) if self.__selectedItem(): self.__selectedItem().setSelected(False) if uuid: for i in range(self.__lstNotes.count()): item = self.__lstNotes.item(i) if item.note().uuid == uuid: item.setSelected(True) break self.__lstNotes.setDisabled(False) def __selectedItem(self): selectedItems = self.__lstNotes.selectedItems() return selectedItems[0] if selectedItems else None def __refreshNoteButtons(self): disableButtons = not self.__viewerEditor.noteUuid() self.__btnEdit.setDisabled(disableButtons) self.__btnDelete.setDisabled(disableButtons) def __refreshTagCombo(self): tags = self.__tags[:] tags.insert(0, "") currentTag = self.__cmbTag.currentText() self.__cmbTag.clear() self.__cmbTag.addItems(tags) if currentTag and currentTag in tags: self.__cmbTag.setCurrentIndex(tags.index(currentTag)) def __refreshNoteList(self, selectNoteOnViewerEditor=False): notes = self.__notes searchText = self.textFilter() if searchText: uuids = self.noteProvider().search(searchText) notes = list(filter(lambda n: n.uuid in uuids, notes)) currentTag = self.__cmbTag.currentText() if currentTag: notes = list(filter(lambda n: n.hasTag(currentTag), notes)) selectUuid = None if selectNoteOnViewerEditor: selectUuid = self.__viewerEditor.noteUuid() elif self.__selectedItem(): selectUuid = self.__selectedItem().note().uuid elif len(notes) > 0: selectUuid = notes[0].uuid self.__lstNotes.setDisabled(True) self.__lstNotes.clear() selectedNoteItem = None for note in notes: item = ListItem(note, self.__lstNotes) if selectUuid == note.uuid: item.setSelected(True) selectedNoteItem = item if selectedNoteItem: self.__lstNotes.setCurrentItem(selectedNoteItem) self.__lstNotes.setDisabled(False) def __refreshNoteViewerEditor(self): if self.__selectedItem(): note = self.noteProvider().get(self.__selectedItem().note().uuid) else: note = renderHtml(Note()) self.__viewerEditor.view(note) def __onNewNoteClicked(self): if self.__viewerEditor.isAllowedToChange(): self.__lstNotes.setCurrentItem(None) self.__viewerEditor.edit(renderHtml(Note())) self.__refreshNoteButtons() def __onEditNoteClicked(self): if self.__viewerEditor.isAllowedToChange(): self.__viewerEditor.edit() def __onDeleteNoteClicked(self): # noinspection PyCallByClass choice = QMessageBox.question(self, "Delete note", "Are you ABSOLUTELY sure?", buttons=QMessageBox.Yes | QMessageBox.No, defaultButton=QMessageBox.No) if choice != QMessageBox.Yes: return note = Note(self.__viewerEditor.noteUuid(), datetime.utcnow().replace(microsecond=0)) self.noteProvider().remove(note) self.__setSelectedItem() self.reload() self.showStatus("Note deleted") self.__lstNotes.setFocus() def __onSyncClicked(self): if self.remoteNoteProvider() and self.__viewerEditor.isAllowedToChange( ): self.showStatus("Starting synchronization, please wait...", immediate=True) try: Synchronizer(self.noteProvider(), self.remoteNoteProvider()).sync() self.reload(True) self.showStatus("Synchronization finished") except TokenExpiredError: self.showError( "Authentication expired. Synchronization account must be configured again" ) self.disableSync() except AConnectionError: self.showError("Failed to connect to server") except InvalidProxyError: self.showError("Proxy settings are invalid") def __onPreferencesClicked(self): Preferences(self).show() def __onSearchEdited(self): self.__refreshNoteList(True) self.__viewerEditor.updateHighlight() def __onTagFilterSelectionChanged(self): self.__refreshNoteList(True) def __onTagFilterShortcutPressed(self): self.__cmbTag.setFocus() self.__cmbTag.showPopup() def __onNoteSelectionChanged(self): if self.__lstNotes.isEnabled() and self.__selectedItem(): if self.__viewerEditor.isAllowedToChange(): note = self.noteProvider().get( self.__selectedItem().note().uuid) self.__viewerEditor.view(note) self.__refreshNoteButtons() else: self.__setSelectedItem(self.__viewerEditor.noteUuid())
class Window(QWidget): tagsChanged= Signal(list) def __init__(self, appPath): QWidget.__init__(self) rc.init(normpath(appPath + "/resources")) self.__config= Config(appPath) self.__noteProvider= SqliteNoteProvider(normpath(appPath + "/resources/notes/notes.db")) self.setWindowTitle("Scroll, Quill & INK") self.setMinimumSize(400, 240) self.resize(800, 600) icon= QIcon() icon.addPixmap(rc.pixmap("sqink-16x16.png")) icon.addPixmap(rc.pixmap("sqink-32x32.png")) self.setWindowIcon(icon) layout= QVBoxLayout() layout.setContentsMargins(5, 5, 5, 2) self.setLayout(layout) splitter= QSplitter() splitter.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) splitter.setStyle(QStyleFactory.create("Cleanlooks")) layout.addWidget(splitter) self.__statusBar= QStatusBar() self.__statusBar.setSizeGripEnabled(False) layout.addWidget(self.__statusBar) self.__lblNotesCount= QLabel() self.__lblNotesCount.setStyleSheet("border-radius:0.5em; background-color:#b2dfdb; padding:0 0.5em;") self.__statusBar.addPermanentWidget(self.__lblNotesCount) self.__lblError= QLabel() self.__lblError.setStyleSheet("border-radius:0.5em; background-color:#f36c60; padding:0 0.5em;") self.__lblError.setVisible(False) self.__statusBar.addWidget(self.__lblError) panelA= QWidget() splitter.addWidget(panelA) layoutA= QGridLayout() layoutA.setContentsMargins(0, 0, 0, 0) panelA.setLayout(layoutA) barButtons= QWidget() layoutButtons= QHBoxLayout() layoutButtons.setContentsMargins(0, 0, 0, 0) barButtons.setLayout(layoutButtons) layoutA.addWidget(barButtons, 0, 0, 1, 2) btnNew= QPushButton() btnNew.setIcon(rc.icon("note-new-16x16.png")) btnNew.setToolTip("New note (CTRL + N)") btnNew.clicked.connect(self.__onNewNoteClicked) btnNew.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_N)) layoutButtons.addWidget(btnNew) self.__btnEdit= QPushButton() self.__btnEdit.setIcon(rc.icon("note-edit-16x16.png")) self.__btnEdit.setToolTip("Edit note (CTRL + E)") self.__btnEdit.clicked.connect(self.__onEditNoteClicked) self.__btnEdit.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_E)) layoutButtons.addWidget(self.__btnEdit) self.__btnDelete= QPushButton() self.__btnDelete.setIcon(rc.icon("note-delete-16x16.png")) self.__btnDelete.setToolTip("Delete note (CTRL + L)") self.__btnDelete.clicked.connect(self.__onDeleteNoteClicked) self.__btnDelete.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_L)) layoutButtons.addWidget(self.__btnDelete) layoutButtons.addStretch(1) self.__btnSync= QPushButton() self.__btnSync.setIcon(rc.icon("sync-16x16.png")) self.__btnSync.setToolTip("Sync (CTRL + I)") self.__btnSync.clicked.connect(self.__onSyncClicked) self.__btnSync.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_I)) layoutButtons.addWidget(self.__btnSync) btnConfig= QPushButton() btnConfig.setIcon(rc.icon("preferences-16x16.png")) btnConfig.setToolTip("Preferences") btnConfig.clicked.connect(self.__onPreferencesClicked) layoutButtons.addWidget(btnConfig) lblSearch= QLabel() lblSearch.setPixmap(rc.pixmap("search-16x16.png")) lblSearch.setToolTip("Find (CTRL + F)") layoutA.addWidget(lblSearch, 1, 0) self.__txtSearch= QLineEdit() self.__txtSearch.setValidator(QRegExpValidator(QRegExp("[A-ZÁÉÍÓÚÜÑa-záéíóúüñ0-9 @:/#&=\.\-\?\\\\]+"))) self.__txtSearch.textEdited.connect(self.__onSearchEdited) QShortcut(QKeySequence(Qt.CTRL + Qt.Key_F), self.__txtSearch, self.__txtSearch.setFocus) layoutA.addWidget(self.__txtSearch, 1, 1) lblTag= QLabel() lblTag.setPixmap(rc.pixmap("tag-16x16.png")) lblTag.setToolTip("Filter by tag (CTRL + T)") layoutA.addWidget(lblTag, 2, 0) self.__cmbTag= QComboBox() self.__cmbTag.activated.connect(self.__onTagFilterSelectionChanged) QShortcut(QKeySequence(Qt.CTRL + Qt.Key_T), self.__cmbTag, self.__onTagFilterShortcutPressed) layoutA.addWidget(self.__cmbTag, 2, 1) self.__lstNotes= ListWidget(self.__onDeleteNoteClicked) self.__lstNotes.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.__lstNotes.setItemDelegate(ListItemDelegate()) self.__lstNotes.itemSelectionChanged.connect(self.__onNoteSelectionChanged) layoutA.addWidget(self.__lstNotes, 3, 0, 1, 2) self.__viewerEditor= ViewerEditor(self) splitter.addWidget(self.__viewerEditor) self.configureSync() self.reload() self.__txtSearch.setFocus() def closeEvent(self, event): self.__noteProvider.close() QWidget.closeEvent(self, event) def config(self): return self.__config def noteProvider(self): return self.__noteProvider def remoteNoteProvider(self): return self.__remoteNoteProvider def textFilter(self): return self.__txtSearch.text().strip() def configureSync(self): config= self.config() if config.isDropboxEnabled(): self.__remoteNoteProvider= DropboxNoteProvider(config.getDropboxToken(), config.getDropboxFolder(), config.getProxyHost(), config.getProxyPort(), config.getProxyUser(), config.getProxyPassword()) self.__btnSync.setDisabled(False) elif config.isGoogleDriveEnabled(): self.__remoteNoteProvider= GoogleDriveNoteProvider(config.getGoogleDriveToken(), config.getProxyHost(), config.getProxyPort(), config.getProxyUser(), config.getProxyPassword()) self.__btnSync.setDisabled(False) else: self.__remoteNoteProvider= None self.__btnSync.setDisabled(True) def disableSync(self): config= self.config() if config.isDropboxEnabled(): config.setDropboxEnabled(False) config.setDropboxToken(None) else: config.setGoogleDriveEnabled(False) config.setGoogleDriveToken(None) config.save() self.configureSync() def updateProxy(self): self.__viewerEditor.updateProxy() def showStatus(self, message, seconds=5): self.clearError() self.__statusBar.showMessage(message, timeout=seconds * 1000) def showError(self, message): self.__statusBar.clearMessage() self.__lblError.setText(message) self.__lblError.setVisible(True) def clearError(self): self.__lblError.setVisible(False) def reload(self, selectNoteOnViewerEditor=False, skipViewerEditor=False): self.__notes= sorted(self.noteProvider().list(), key=lambda n: n.title.lower()) self.__tags= listTags(self.__notes) self.__refreshTagCombo() self.__refreshNoteList(selectNoteOnViewerEditor) self.__lblNotesCount.setText("%d notes" % len(self.__notes)) if not skipViewerEditor: self.__refreshNoteViewerEditor() self.__refreshNoteButtons() self.tagsChanged.emit(self.__tags) def __setSelectedItem(self, uuid=None): self.__lstNotes.setDisabled(True) if self.__selectedItem(): self.__selectedItem().setSelected(False) if uuid: for i in range(self.__lstNotes.count()): item= self.__lstNotes.item(i) if (item.note().uuid == uuid): item.setSelected(True) break self.__lstNotes.setDisabled(False) def __selectedItem(self): selectedItems= self.__lstNotes.selectedItems() return selectedItems[0] if selectedItems else None def __refreshNoteButtons(self): disableButtons= not self.__viewerEditor.noteUuid() self.__btnEdit.setDisabled(disableButtons) self.__btnDelete.setDisabled(disableButtons) def __refreshTagCombo(self): tags= self.__tags[:] tags.insert(0, "") currentTag= self.__cmbTag.currentText() self.__cmbTag.clear() self.__cmbTag.addItems(tags) if currentTag and currentTag in tags: self.__cmbTag.setCurrentIndex(tags.index(currentTag)) def __refreshNoteList(self, selectNoteOnViewerEditor=False): notes= self.__notes searchText= self.textFilter() if searchText: uuids= self.noteProvider().search(searchText) notes= list(filter(lambda n: n.uuid in uuids, notes)) currentTag= self.__cmbTag.currentText() if currentTag: notes= list(filter(lambda n: n.hasTag(currentTag), notes)) selectUuid= None if selectNoteOnViewerEditor: selectUuid= self.__viewerEditor.noteUuid() elif self.__selectedItem(): selectUuid= self.__selectedItem().note().uuid elif len(notes) > 0: selectUuid= notes[0].uuid self.__lstNotes.setDisabled(True) self.__lstNotes.clear() selectedNoteItem= None for note in notes: item= ListItem(note, self.__lstNotes) if selectUuid == note.uuid: item.setSelected(True) selectedNoteItem= item if selectedNoteItem: self.__lstNotes.setCurrentItem(selectedNoteItem) self.__lstNotes.setDisabled(False) def __refreshNoteViewerEditor(self): if self.__selectedItem(): note= self.noteProvider().get(self.__selectedItem().note().uuid) else: note= renderHtml(Note()) self.__viewerEditor.view(note) def __onNewNoteClicked(self): if self.__viewerEditor.isAllowedToChange(): self.__lstNotes.setCurrentItem(None) self.__viewerEditor.edit(renderHtml(Note())) self.__refreshNoteButtons() def __onEditNoteClicked(self): if self.__viewerEditor.isAllowedToChange(): self.__viewerEditor.edit() def __onDeleteNoteClicked(self): choice= QMessageBox.question(self, "Delete note", "Are you ABSOLUTELY sure?", buttons=QMessageBox.Yes | QMessageBox.No, defaultButton=QMessageBox.No) if choice != QMessageBox.Yes: return note= Note(self.__viewerEditor.noteUuid(), datetime.utcnow().replace(microsecond=0)) self.noteProvider().remove(note) self.__setSelectedItem() self.reload() self.showStatus("Note deleted") self.__lstNotes.setFocus() def __onSyncClicked(self): if self.remoteNoteProvider() and self.__viewerEditor.isAllowedToChange(): self.showStatus("Starting synchronization, please wait...") try: Synchronizer(self.noteProvider(), self.remoteNoteProvider()).sync() self.reload(True) self.showStatus("Synchronization finished") except TokenExpiredError: self.showError("Authentication expired. Synchronization account must be configured again") self.disableSync() except AConnectionError: self.showError("Failed to connect to server") except InvalidProxyError: self.showError("Proxy settings are invalid") def __onPreferencesClicked(self): Preferences(self).show() def __onSearchEdited(self): self.__refreshNoteList(True) self.__viewerEditor.updateHighlight() def __onTagFilterSelectionChanged(self): self.__refreshNoteList(True) def __onTagFilterShortcutPressed(self): self.__cmbTag.setFocus() self.__cmbTag.showPopup() def __onNoteSelectionChanged(self): if self.__lstNotes.isEnabled() and self.__selectedItem(): if self.__viewerEditor.isAllowedToChange(): note= self.noteProvider().get(self.__selectedItem().note().uuid) self.__viewerEditor.view(note) self.__refreshNoteButtons() else: self.__setSelectedItem(self.__viewerEditor.noteUuid())