├── FileAll.py ├── GitNote.py ├── GitNote.ui ├── GitNoteUi.py ├── GitNote_back.ui ├── Init.py ├── LogOn.py ├── LogOn.ui ├── LogOnUi.py ├── README.md ├── __pycache__ ├── FileAll.cpython-36.pyc ├── GitNote.cpython-36.pyc ├── GitNoteUi.cpython-36.pyc ├── Init.cpython-36.pyc ├── LogOn.cpython-36.pyc ├── LogOnUi.cpython-36.pyc └── main.cpython-36.pyc ├── addpicture.png ├── app.ico ├── askpass.py ├── blackdir.ico ├── config.ico ├── convert.ico ├── dir.ico ├── dir_back.ico ├── edit.ico ├── loading.gif ├── loading.png ├── main.py ├── pictures ├── gitnote-1.png ├── gitnote-10.png ├── gitnote-11.png ├── gitnote-12.png ├── gitnote-2.png ├── gitnote-3.png ├── gitnote-4.png ├── gitnote-5.png ├── gitnote-6.png ├── gitnote-7.png ├── gitnote-8.png └── gitnote-9.png └── save.ico /FileAll.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import main 5 | import getpass, os 6 | 7 | def storeConfig(): 8 | writeHandle = open(os.path.join(main.gitNoteHome, "config"), 'w') 9 | writeHandle.write(main.userName + '\n') 10 | writeHandle.write(main.password + '\n') 11 | writeHandle.write(main.gitUrl + '\n') 12 | writeHandle.close() 13 | 14 | def readConfig(): 15 | readHandle = open(os.path.join(main.gitNoteHome, "config"), 'r') 16 | main.userName = readHandle.readline().strip() 17 | main.password = readHandle.readline().strip() 18 | main.gitUrl = readHandle.readline().strip() 19 | -------------------------------------------------------------------------------- /GitNote.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | 4 | from PyQt5.QtWidgets import QWidget, QApplication, QMainWindow, QTreeWidgetItem, QListWidgetItem, QMenu, QInputDialog, QMessageBox, QFileDialog, QToolButton, QFontDialog, QColorDialog 5 | from PyQt5.QtGui import QIcon, QColor, QBrush, QPalette, QColor, QFontMetricsF, QPixmap, QMovie, QTextCursor, QFont 6 | from PyQt5.QtCore import Qt, QByteArray, QThread, QTimer, QSize 7 | import GitNoteUi 8 | import main 9 | import git 10 | import os, getpass, threading, time, datetime, operator, shutil 11 | import pathlib 12 | import mistune 13 | import json, pdfkit 14 | 15 | movieStatus = False 16 | 17 | class CloneThread(QThread): 18 | def __init__(self): 19 | super(CloneThread, self).__init__() 20 | 21 | def run(self): 22 | global movieStatus 23 | main.setGitEnv() 24 | git.Repo.clone_from(url=main.gitUrl, to_path=main.gitNoteNoteHome) 25 | movieStatus = True 26 | #main.myGitNote.setUpdateBack() 27 | 28 | class UpdateThread(QThread): 29 | def __init__(self): 30 | super(UpdateThread, self).__init__() 31 | 32 | def run(self): 33 | global movieStatus 34 | main.setGitEnv() 35 | repo = git.Repo(main.gitNoteNoteHome) 36 | remote = repo.remote() 37 | repo.git.add('--all') 38 | repo.index.commit('note') 39 | remote.push() 40 | remote.pull() 41 | movieStatus = True 42 | #main.myGitNote.setUpdateBack() 43 | 44 | class GitNote(QWidget, GitNoteUi.Ui_Form_note): 45 | def __init__(self, parent=None): 46 | super(GitNote, self).__init__(parent) 47 | self.setupUi(self) 48 | self.initUi() 49 | self.show() 50 | self.updateUiAfterShow() 51 | 52 | def initUi(self): 53 | if not main.gitExist: 54 | self.mycloneGit() 55 | # 界面配置 56 | self.configfile = os.path.join(main.gitNoteHome, "config.json") 57 | self.initInterface() 58 | self.pushButton_update.clicked.connect(self.myupdateGit) 59 | self.treeWidget_tree.clicked.connect(self.onTreeClicked) 60 | self.treeWidget_tree.customContextMenuRequested.connect(self.menuTreeContextClicked) 61 | self.listWidget_list.clicked.connect(self.clickedListView) 62 | self.listWidget_list.customContextMenuRequested.connect(self.menuListContextClicked) 63 | #self.treeWidget_tree.addTopLevelItem(root) 64 | # set window background color 65 | self.setAutoFillBackground(True) 66 | self.listfileDir = main.gitNoteNoteHome 67 | self.viewfileName = "" 68 | self.viewTexts = "" 69 | self.plainTextEdit_markdown.hide() 70 | self.plainTextEdit_markdown.setTabStopDistance(QFontMetricsF(self.plainTextEdit_markdown.font()).width(' ')*4) 71 | self.saveStatus = False 72 | self.createStatus = False 73 | self.pushButton_save.clicked.connect(self.clickedButtonSave) 74 | self.plainTextEdit_markdown.textChanged.connect(self.textChangedEdit) 75 | self.pushButton_save.setEnabled(False) 76 | self.updateListView(self.listfileDir) 77 | self.newDirName = "" 78 | self.pushButton_addpicture.setEnabled(False) 79 | self.pushButton_addpicture.clicked.connect(self.choosePictures) 80 | self.insertPictures = [] 81 | # 设置更新图标 82 | icon = QIcon() 83 | icon.addPixmap(QPixmap(os.path.join(os.path.dirname(__file__), "loading.png")), QIcon.Normal, QIcon.Off) 84 | self.pushButton_update.setIcon(icon) 85 | saveicon = QIcon() 86 | saveicon.addPixmap(QPixmap(os.path.join(os.path.dirname(__file__), "edit.ico")), QIcon.Normal, QIcon.Off) 87 | self.pushButton_save.setIcon(saveicon) 88 | addpicturicon = QIcon() 89 | addpicturicon.addPixmap(QPixmap(os.path.join(os.path.dirname(__file__), "addpicture.png")), QIcon.Normal, QIcon.Off) 90 | self.pushButton_addpicture.setIcon(addpicturicon) 91 | # 保存打开时的文本 92 | self.oldTexts = "" 93 | # 更新恢复 94 | self.movietimer = QTimer(self) 95 | self.movietimer.start(500) 96 | self.movietimer.timeout.connect(self.movieTimeout) 97 | # 配置 98 | configicon = QIcon() 99 | configicon.addPixmap(QPixmap(os.path.join(os.path.dirname(__file__), "config.ico")), QIcon.Normal, QIcon.Off) 100 | self.toolButton_config.setIcon(configicon) 101 | toolmenu = QMenu() 102 | toolmenu.addAction("设置字体", self.setFont) 103 | toolmenu.addAction("默认主题", self.whiteTheme) 104 | toolmenu.addAction("暗黑主题", self.blackTheme) 105 | self.toolButton_config.setMenu(toolmenu) 106 | self.toolButton_config.setPopupMode(QToolButton.MenuButtonPopup) 107 | # 转换 108 | convertico = QIcon(os.path.join(os.path.dirname(__file__), "convert.ico")) 109 | self.toolButton_functions.setIcon(convertico) 110 | convertmenu = QMenu() 111 | convertmenu.addAction("另存为pdf文件", self.viewToPdf) 112 | self.toolButton_functions.setMenu(convertmenu) 113 | 114 | def viewToPdf(self): 115 | if not self.pushButton_save.isEnabled() and not self.pushButton_addpicture.isEnabled(): 116 | return 117 | filename, filetype = QFileDialog.getSaveFileName(self, "文件保存", str(pathlib.Path.home()), "PdfFiles (*.pdf)") 118 | if filename != "": 119 | if filename[-4:] != '.pdf': 120 | oldfilename = filename 121 | filename = filename + '.pdf' 122 | if os.path.exists(filename) and not os.path.exists(oldfilename): 123 | replay = QMessageBox.question(self, "文件覆盖警告", "文件"+os.path.basename(filename)+"已存在,确定覆盖?", QMessageBox.Yes, QMessageBox.No) 124 | if replay == QMessageBox.No: 125 | return 126 | markdown = mistune.Markdown() 127 | pdfkit.from_string('' + markdown(self.showRealPictures(self.viewTexts)), filename) 128 | #print(markdown(self.showRealPictures(self.viewTexts))) 129 | 130 | def initInterface(self): 131 | self.interfacedata = {'theme': 'white'} 132 | if not os.path.exists(self.configfile): 133 | self.whiteTheme() 134 | return 135 | with open(self.configfile, 'r') as f: 136 | self.interfacedata = json.load(f) 137 | if 'theme' in self.interfacedata and self.interfacedata['theme'] == 'black': 138 | self.blackTheme() 139 | else: 140 | self.whiteTheme() 141 | if 'font' in self.interfacedata: 142 | font = QFont() 143 | font.fromString(self.interfacedata['font']) 144 | self.plainTextEdit_markdown.setFont(font) 145 | self.textEdit_show.setFont(font) 146 | 147 | def whiteTheme(self): 148 | self.dirIcon = QIcon(os.path.join(os.path.dirname(__file__),"dir.ico")) 149 | self.addTopDirs() 150 | pBack = self.palette() 151 | pBack.setColor(self.backgroundRole(), QColor(239, 235, 231)) 152 | self.setPalette(pBack) 153 | self.treeWidget_tree.setStyleSheet('background-color: rgb(255, 255, 255);color: rgb(0, 0, 0)') 154 | self.listWidget_list.setStyleSheet("QListWidget{background-color: rgb(255, 255, 255);color: rgb(0, 0, 0);}" 155 | "QListWidget::item { border-bottom: 0.5px dotted black; margin-bottom:10px;}" 156 | "QListWidget::item:!selected{}" 157 | "QListWidget::item:selected:active{background:#FFFFFF;color:#19649F;border-width:-1;}" 158 | "QListWidget::item:selected{background:#FFFFFF;color:#19649F;}") 159 | self.lineEdit_title.setStyleSheet('background-color: rgb(255, 255, 255);color: rgb(0, 0, 0)') 160 | self.plainTextEdit_markdown.setStyleSheet('background-color: rgb(255, 255, 255);color: rgb(0, 0, 0)') 161 | self.textEdit_show.setStyleSheet('background-color: rgb(255, 255, 255);color: rgb(0, 0, 0)') 162 | if self.interfacedata['theme'] != 'white': 163 | self.interfacedata['theme'] = 'white' 164 | with open(self.configfile, 'w') as f: 165 | json.dump(self.interfacedata, f) 166 | 167 | def blackTheme(self): 168 | self.dirIcon = QIcon(os.path.join(os.path.dirname(__file__),"blackdir.ico")) 169 | self.addTopDirs() 170 | pBack = self.palette() 171 | pBack.setColor(self.backgroundRole(), QColor(51, 51, 51)) 172 | self.setPalette(pBack) 173 | self.treeWidget_tree.setStyleSheet('background-color: rgb(51, 51, 51);color: rgb(200, 200, 200);') 174 | self.listWidget_list.setStyleSheet("QListWidget{background-color: rgb(37, 37, 38);color: rgb(200, 200, 200);}" 175 | "QListWidget::item { border-bottom: 0.5px dotted white; margin-bottom:10px;}") 176 | self.lineEdit_title.setStyleSheet('background-color: rgb(30, 30, 30);color: rgb(200, 200, 200)') 177 | self.plainTextEdit_markdown.setStyleSheet('background-color: rgb(30, 30, 30);color: rgb(200, 200, 200)') 178 | self.textEdit_show.setStyleSheet('background-color: rgb(30, 30, 30);color: rgb(200, 200, 200)') 179 | if self.interfacedata['theme'] != 'black': 180 | self.interfacedata['theme'] = 'black' 181 | with open(self.configfile, 'w') as f: 182 | json.dump(self.interfacedata, f) 183 | 184 | def setFont(self): 185 | font, ok = QFontDialog.getFont() 186 | if ok: 187 | self.plainTextEdit_markdown.setFont(font) 188 | self.textEdit_show.setFont(font) 189 | self.interfacedata['font'] = font.toString() 190 | with open(self.configfile, 'w') as f: 191 | json.dump(self.interfacedata, f) 192 | 193 | def movieTimeout(self): 194 | global movieStatus 195 | if movieStatus: 196 | movieStatus = False 197 | self.setUpdateBack() 198 | 199 | def closeEvent(self, event): 200 | if self.saveStatus: 201 | unsavereturn = self.saveNote(False) 202 | if not unsavereturn: 203 | replay = QMessageBox.question(self, "未保存警告", "有未保存的未命名笔记,确定退出?", QMessageBox.Yes, QMessageBox.No) 204 | if replay == QMessageBox.Yes: 205 | event.accept() 206 | return 207 | elif replay == QMessageBox.No: 208 | event.ignore() 209 | return 210 | #self.saveNote(True) 211 | event.accept() 212 | 213 | def keyPressEvent(self, event): 214 | if (event.key() == Qt.Key_O): 215 | if QApplication.keyboardModifiers() == Qt.ControlModifier: 216 | self.pushButton_save.clicked.emit() 217 | 218 | def choosePictures(self): 219 | pictures, ok = QFileDialog.getOpenFileNames(self, "选取图片", str(pathlib.Path.home()), "Picture Files (*.png | *.jpg | *.jpeg | *.gif | *.ico | *.svg)") 220 | for eachfile in pictures: 221 | basenamewith = os.path.basename(eachfile) 222 | basename, suffix = os.path.splitext(basenamewith) 223 | i = 0 224 | while os.path.exists(os.path.join(self.showTextDir, basename+"-"+str(i)+suffix)): 225 | i = i + 1 226 | lastbasename =basename + "-" + str(i) + suffix 227 | self.insertPictures.append(lastbasename) 228 | self.plainTextEdit_markdown.insertPlainText("\n![](" + lastbasename + ")\n") 229 | lastname = os.path.join(self.showTextDir, lastbasename) 230 | shutil.copyfile(eachfile, lastname) 231 | time.sleep(0.01) 232 | 233 | def moveDirToDir(self): 234 | dir_choose = QFileDialog.getExistingDirectory(self, "选择目标文件夹", main.gitNoteNoteHome) 235 | if self.listfileDir in dir_choose: 236 | QMessageBox.information(self, "警告", "文件夹不能移动到当前目录和子目录!", QMessageBox.Yes) 237 | return 238 | targetDir = self.getTargetName(dir_choose, os.path.basename(self.listfileDir)) 239 | if os.path.exists(self.listfileDir): 240 | shutil.move(self.listfileDir, targetDir) 241 | if self.listfileDir in self.viewfileName: 242 | self.lineEdit_title.clear() 243 | self.plainTextEdit_markdown.clear() 244 | self.textEdit_show.clear() 245 | self.listfileDir = main.gitNoteNoteHome 246 | self.addTopDirs() 247 | self.listWidget_list.clear() 248 | self.pushButton_save.setEnabled(False) 249 | 250 | def deleteDir(self): 251 | countNotes = 0 252 | for dirpath, dirnames, filenames in os.walk(self.listfileDir): 253 | for eachone in filenames: 254 | if os.path.splitext(eachone)[1] == ".md": 255 | countNotes = countNotes + 1 256 | replay = QMessageBox.warning(self, "警告", "文件夹 " + os.path.basename(os.path.normpath(self.listfileDir)) + "下有" + str(countNotes) + "篇笔记,您确定要删除吗?", QMessageBox.Yes|QMessageBox.No, QMessageBox.No) 257 | if replay == QMessageBox.Yes and os.path.exists(self.listfileDir): 258 | shutil.rmtree(self.listfileDir) 259 | if self.listfileDir in self.viewfileName: 260 | self.lineEdit_title.clear() 261 | self.plainTextEdit_markdown.clear() 262 | self.textEdit_show.clear() 263 | self.listfileDir = main.gitNoteNoteHome 264 | self.addTopDirs() 265 | self.listWidget_list.clear() 266 | self.pushButton_save.setEnabled(False) 267 | 268 | def getPicturesInOneNote(self, filename): 269 | pictures = [] 270 | tmpf = open(filename, "r", encoding='UTF-8') 271 | tmpviewTexts = tmpf.read() 272 | tmpf.close() 273 | multilines = tmpviewTexts.split("\n") 274 | for eachline in multilines: 275 | if "![](" in eachline and ")" in eachline.split("![](")[1]: 276 | shotfile = (eachline.split("![](")[1]).split(")")[0] 277 | realfile = os.path.join(self.listfileDir, shotfile) 278 | if os.path.exists(realfile): 279 | pictures.append(realfile) 280 | return pictures 281 | 282 | def deleteNote(self): 283 | if not os.path.exists(self.listmenufilename): 284 | return 285 | basename = os.path.basename(self.listmenufilename) 286 | realname = os.path.splitext(basename)[0] 287 | replay = QMessageBox.warning(self, "警告", "您确定要删除笔记 "+realname+" 吗?", QMessageBox.Yes|QMessageBox.No, QMessageBox.No) 288 | if replay == QMessageBox.Yes: 289 | # 先删除里面的图片 290 | pictures = self.getPicturesInOneNote(self.listmenufilename) 291 | for eachpicture in pictures: 292 | os.remove(eachpicture) 293 | # 然后删除markdown文件和更新控件显示 294 | os.remove(self.listmenufilename) 295 | self.addTopDirs() 296 | if os.path.exists(self.listfileDir): 297 | self.updateListView(self.listfileDir) 298 | if self.listmenufilename.strip() == self.viewfileName.strip(): 299 | self.clearNoteShow() 300 | self.listmenufilename = "" 301 | 302 | def createRootDir(self): 303 | newDirName, ok = QInputDialog.getText(self, "创建新文件夹", "文件夹名") 304 | if len(newDirName) > 0: 305 | newWholeDirName = os.path.join(main.gitNoteNoteHome, newDirName) 306 | if not os.path.exists(newWholeDirName): 307 | os.mkdir(newWholeDirName) 308 | self.addTopDirs() 309 | 310 | def createDir(self): 311 | newDirName, ok = QInputDialog.getText(self, "创建新文件夹", "文件夹名") 312 | if len(newDirName) > 0: 313 | newWholeDirName = os.path.join(self.listfileDir, newDirName) 314 | if not os.path.exists(newWholeDirName): 315 | os.mkdir(newWholeDirName) 316 | self.addTopDirs() 317 | 318 | def menuListContextClicked(self, pos): 319 | item = self.listWidget_list.itemAt(pos) 320 | if item: 321 | itemCount = item.text() 322 | filename = (itemCount.split("\n"))[0] 323 | #self.viewfileName = os.path.join(self.listfileDir, filename) 324 | #if self.viewfileName[-3:] != ".md": 325 | # self.viewfileName = self.viewfileName + ".md" 326 | self.listmenufilename = os.path.join(self.listfileDir, filename) 327 | if self.listmenufilename[-3:] != ".md": 328 | self.listmenufilename = self.listmenufilename + ".md" 329 | self.listmenu = QMenu() 330 | self.listmenu.addAction("删除笔记", self.deleteNote) 331 | self.listmenu.addAction("移动笔记", self.moveNoteToDir) 332 | self.listmenu.addAction("重命名笔记", self.renameNote) 333 | self.listmenu.exec_(self.listWidget_list.mapToGlobal(pos)) 334 | 335 | def clearNoteShow(self): 336 | self.pushButton_save.setEnabled(False) 337 | self.lineEdit_title.clear() 338 | self.lineEdit_title.setReadOnly(True) 339 | self.pushButton_addpicture.setEnabled(False) 340 | self.plainTextEdit_markdown.clear() 341 | self.plainTextEdit_markdown.hide() 342 | self.textEdit_show.clear() 343 | self.viewfileName = "" 344 | 345 | def renameNote(self): 346 | newname, ok = QInputDialog.getText(self, "笔记重命名", "请输入新名:") 347 | newname = newname + ".md" 348 | if ok: 349 | newfilepath = os.path.join(self.listfileDir, newname) 350 | if os.path.exists(newfilepath): 351 | QMessageBox.warning(self, "警告", "笔记已存在,请重新命名", QMessageBox.Yes, QMessageBox.Yes) 352 | return 353 | if os.path.exists(self.listmenufilename): 354 | shutil.move(self.listmenufilename, newfilepath) 355 | if self.viewfileName.strip() == self.listmenufilename.strip(): 356 | realname = os.path.splitext(newname)[0] 357 | self.lineEdit_title.setText(realname) 358 | self.viewfileName = newfilepath 359 | if os.path.exists(self.listfileDir): 360 | self.updateListView(self.listfileDir) 361 | 362 | def getTargetName(self, targetDir, basenamewith): 363 | if os.path.exists(os.path.join(targetDir, basenamewith)): 364 | basename, suffix = os.path.splitext(basenamewith) 365 | i = 0 366 | while os.path.exists(os.path.join(targetDir, basename+"-"+str(i)+suffix)): 367 | i = i + 1 368 | basenamewith = basename + "-" + str(i) + suffix 369 | return os.path.join(targetDir, basenamewith) 370 | 371 | def replacePictureName(self, viewTexts, basename, newbasename): 372 | multilines = viewTexts.split("\n") 373 | newViewTexts = "" 374 | for eachline in multilines: 375 | if "![](" in eachline and ")" in eachline.split("![](")[1] and (eachline.split("![](")[1]).split(")")[0] == basename: 376 | newViewTexts = newViewTexts + "![]("+newbasename+")\n" 377 | else: 378 | newViewTexts = newViewTexts + eachline+"\n" 379 | return newViewTexts 380 | 381 | def moveNoteToDir(self): 382 | dir_choose = QFileDialog.getExistingDirectory(self, "选择目标文件夹", main.gitNoteNoteHome) 383 | if main.gitNoteNoteHome in dir_choose and os.path.dirname(self.listmenufilename) != dir_choose: 384 | # 移动图片 385 | tmpf = open(self.listmenufilename, "r", encoding='UTF-8') 386 | tmpviewTexts = tmpf.read() 387 | tmpf.close() 388 | pictures = self.getPicturesInOneNote(self.listmenufilename) 389 | for eachpicture in pictures: 390 | basename = os.path.basename(eachpicture) 391 | targetfilename = self.getTargetName(dir_choose, basename) 392 | if os.path.exists(eachpicture): 393 | shutil.move(eachpicture, targetfilename) 394 | newbasename = os.path.basename(targetfilename) 395 | tmpviewTexts= self.replacePictureName(tmpviewTexts, basename, newbasename) 396 | tmpf = open(self.listmenufilename, "w", encoding='UTF-8') 397 | tmpf.write(tmpviewTexts) 398 | tmpf.close() 399 | # 移动日记文件 400 | basename = os.path.basename(self.listmenufilename) 401 | targetfilename = self.getTargetName(dir_choose, basename) 402 | if os.path.exists(self.listmenufilename): 403 | shutil.move(self.listmenufilename, targetfilename) 404 | if self.viewfileName.strip() == self.listmenufilename.strip(): 405 | self.viewfileName = "" 406 | self.clearNoteShow() 407 | self.addTopDirs() 408 | if os.path.exists(self.listfileDir): 409 | self.updateListView(self.listfileDir) 410 | 411 | def renameDir(self): 412 | newname, ok = QInputDialog.getText(self, "笔记重命名", "请输入新名:") 413 | if ok: 414 | if self.listfileDir[-1] == '/' or self.listfileDir[-1] == '\\': 415 | self.listfileDir = self.listfileDir[:-1] 416 | newDir = os.path.join(os.path.dirname(self.listfileDir), newname) 417 | if os.path.exists(newDir): 418 | QMessageBox.warning(self, "警告", "文件夹已存在,请重新命名", QMessageBox.Yes, QMessageBox.Yes) 419 | return 420 | else: 421 | shutil.move(self.listfileDir, newDir) 422 | self.listfileDir = newDir 423 | self.addTopDirs() 424 | self.updateListView(self.listfileDir) 425 | self.clearNoteShow() 426 | 427 | def menuTreeContextClicked(self, pos): 428 | item = self.treeWidget_tree.itemAt(pos) 429 | if item: 430 | self.listfileDir = item.text(1) 431 | self.updateListView(self.listfileDir) 432 | self.treemenu = QMenu() 433 | self.treemenu.addAction("新建笔记", self.createNote) 434 | self.treemenu.addAction("新建日记", self.createDiaryNote) 435 | self.treemenu.addAction("新建文件夹", self.createDir) 436 | self.treemenu.addAction("删除文件夹", self.deleteDir) 437 | self.treemenu.addAction("移动文件夹", self.moveDirToDir) 438 | self.treemenu.addAction("重命名文件夹", self.renameDir) 439 | self.treemenu.exec_(self.treeWidget_tree.mapToGlobal(pos)) 440 | else: 441 | self.listfileDir = main.gitNoteNoteHome 442 | self.treerootmenu = QMenu() 443 | self.treerootmenu.addAction("新建Root文件夹", self.createRootDir) 444 | self.treerootmenu.exec_(self.treeWidget_tree.mapToGlobal(pos)) 445 | 446 | def createNote(self): 447 | self.insertPictures = [] 448 | if self.saveStatus: 449 | self.saveNote(True) 450 | self.saveStatus = True 451 | saveicon = QIcon() 452 | saveicon.addPixmap(QPixmap(os.path.join(os.path.dirname(__file__), "save.ico")), QIcon.Normal, QIcon.Off) 453 | self.pushButton_save.setIcon(saveicon) 454 | self.pushButton_save.setEnabled(True) 455 | self.pushButton_addpicture.setEnabled(True) 456 | self.createStatus = True 457 | self.lineEdit_title.setReadOnly(False) 458 | self.lineEdit_title.clear() 459 | self.plainTextEdit_markdown.clear() 460 | self.plainTextEdit_markdown.show() 461 | self.textEdit_show.clear() 462 | self.lineEdit_title.setFocus() 463 | self.oldTexts = "" 464 | self.showTextDir = self.listfileDir 465 | 466 | def createDiaryNote(self): 467 | today = str(time.strftime("%Y-%m-%d", time.localtime())) 468 | self.createNote() 469 | self.lineEdit_title.setText(today) 470 | self.plainTextEdit_markdown.setFocus() 471 | 472 | def clickedListView(self, qmodelindex): 473 | item = self.listWidget_list.currentItem() 474 | if not item: 475 | return 476 | if self.saveStatus or self.createStatus: 477 | self.saveNote(True) 478 | itemCount = item.text() 479 | filename = (itemCount.split("\n"))[0] 480 | self.pushButton_save.setEnabled(True) 481 | self.viewfileName = os.path.join(self.listfileDir, filename) 482 | if self.viewfileName[-3:] != ".md": 483 | self.viewfileName = self.viewfileName + ".md" 484 | self.lineEdit_title.setText(filename) 485 | tmpf = open(self.viewfileName, "r", encoding='UTF-8') 486 | self.viewTexts = tmpf.read() 487 | tmpf.close() 488 | self.showTextDir = self.listfileDir 489 | #self.textEdit_show.setText(markdown2.markdown(self.showRealPictures(self.viewTexts))) 490 | #self.textEdit_show.setHtml(markdown2.markdown(self.showRealPictures(self.viewTexts))) 491 | markdown = mistune.Markdown() 492 | markdownTxt = markdown(self.showRealPictures(self.viewTexts)) 493 | markdownTxt = markdownTxt.replace("\n<", "$&$&$&").strip() 494 | markdownTxt = markdownTxt.replace("\n", r"
") 495 | markdownTxt = markdownTxt.replace("$&$&$&", "\n<") 496 | if self.interfacedata['theme'] == 'black': 497 | markdownTxt = markdownTxt.replace("", r'') 498 | else: 499 | markdownTxt = markdownTxt.replace("", r'') 500 | self.textEdit_show.setHtml(markdownTxt) 501 | self.textEdit_show.moveCursor(QTextCursor.Start) 502 | 503 | def showRealPictures(self, inputtext): 504 | multilines = inputtext.split("\n") 505 | realtext = "" 506 | for eachline in multilines: 507 | if "![](" in eachline and ")" in eachline.split("![](")[1]: 508 | shotfile = (eachline.split("![](")[1]).split(")")[0] 509 | realfile = os.path.join(self.showTextDir, shotfile) 510 | #realtext = realtext + "![](" + realfile + ")\n" 511 | realtext = realtext + '\n' 512 | else: 513 | realtext = realtext + eachline + "\n" 514 | return realtext 515 | 516 | def textChangedEdit(self): 517 | self.viewTexts = self.plainTextEdit_markdown.toPlainText() 518 | #therealmd = self.showRealPictures(self.viewTexts) 519 | #self.textEdit_show.clear() 520 | #self.textEdit_show.setText(markdown2.markdown(therealmd)) 521 | #self.textEdit_show.setHtml(markdown2.markdown(therealmd)) 522 | #renderer = HighlightRenderer() 523 | #markdown = mistune.Markdown(renderer=renderer) 524 | markdown = mistune.Markdown() 525 | markdownTxt = markdown(self.showRealPictures(self.viewTexts)) 526 | markdownTxt = markdownTxt.replace("\n<", "$&$&$&").strip() 527 | markdownTxt = markdownTxt.replace("\n", r"
") 528 | markdownTxt = markdownTxt.replace("$&$&$&", "\n<") 529 | if self.interfacedata['theme'] == 'black': 530 | markdownTxt = markdownTxt.replace("", r'') 531 | else: 532 | markdownTxt = markdownTxt.replace("", r'') 533 | self.textEdit_show.setHtml(markdownTxt) 534 | #markdown = mistune.Markdown() 535 | #self.textEdit_show.setHtml(markdown(self.showRealPictures(self.viewTexts))) 536 | self.textEdit_show.moveCursor(QTextCursor.End) 537 | 538 | 539 | def clickedButtonSave(self): 540 | if not self.saveStatus: # 编辑 541 | if len(self.viewfileName) > 1: 542 | # 更新已有的图片 543 | self.insertPictures = [] 544 | multilines = self.viewTexts.split("\n") 545 | for eachline in multilines: 546 | if "![](" in eachline and ")" in eachline.split("![](")[1]: 547 | shotfile = (eachline.split("![](")[1]).split(")")[0] 548 | self.insertPictures.append(shotfile) 549 | 550 | self.saveStatus = True 551 | self.plainTextEdit_markdown.setPlainText(self.viewTexts) 552 | self.plainTextEdit_markdown.show() 553 | saveicon = QIcon() 554 | saveicon.addPixmap(QPixmap(os.path.join(os.path.dirname(__file__), "save.ico")), QIcon.Normal, QIcon.Off) 555 | self.pushButton_save.setIcon(saveicon) 556 | self.pushButton_addpicture.setEnabled(True) 557 | self.oldTexts = self.viewTexts 558 | else: #保存 559 | self.pushButton_addpicture.setEnabled(False) 560 | self.saveNote(True) 561 | self.textEdit_show.moveCursor(QTextCursor.Start) 562 | 563 | def saveNote(self, checkwarning): 564 | if len(self.lineEdit_title.text()) < 1: 565 | if checkwarning: 566 | QMessageBox.information(self, "警告", "请输入记录名!", QMessageBox.Yes) 567 | return False 568 | if self.createStatus: 569 | self.viewfileName = os.path.join(self.showTextDir, self.lineEdit_title.text().strip()) 570 | self.lineEdit_title.setReadOnly(True) 571 | if self.viewfileName[-3:] != ".md": 572 | self.viewfileName = self.viewfileName + ".md" 573 | self.saveStatus = False 574 | self.plainTextEdit_markdown.hide() 575 | saveicon = QIcon() 576 | saveicon.addPixmap(QPixmap(os.path.join(os.path.dirname(__file__), "edit.ico")), QIcon.Normal, QIcon.Off) 577 | self.pushButton_save.setIcon(saveicon) 578 | self.lineEdit_title.setReadOnly(True) 579 | if len(self.viewfileName) > 1: 580 | if not self.createStatus and self.oldTexts == self.viewTexts: 581 | return True 582 | tmpf = open(self.viewfileName, "w", encoding='UTF-8') 583 | tmpf.write(self.viewTexts) 584 | tmpf.close() 585 | self.updateListView(self.listfileDir) 586 | #pictures 587 | for eachpicture in self.insertPictures: 588 | if eachpicture not in self.viewTexts and os.path.exists(os.path.join(self.listfileDir, eachpicture)): 589 | os.remove(os.path.join(self.listfileDir, eachpicture)) 590 | #self.treeWidget_tree.clear() 591 | #self.updateTreeItemName() 592 | if self.createStatus: 593 | self.createStatus = False 594 | self.addTopDirs() 595 | return True 596 | 597 | def updateUiAfterShow(self): 598 | self.treeWidget_tree.setColumnWidth(0,100) 599 | 600 | def mycloneGit(self): 601 | if not main.gitExist: 602 | self.movie = QMovie(os.path.join(os.path.dirname(__file__), "loading.gif"), QByteArray(), self) 603 | self.movie.frameChanged.connect(self.setButtonIcon) 604 | self.movie.start() 605 | main.updateStatus = True 606 | self.cloneThread = CloneThread() 607 | self.cloneThread.start() 608 | 609 | def myupdateGit(self): 610 | if main.updateStatus: 611 | return 612 | else: 613 | main.updateStatus = True 614 | self.setUpdateStatus() 615 | self.updateThread = UpdateThread() 616 | self.updateThread.start() 617 | 618 | def setUpdateBack(self): 619 | self.movie = None 620 | icon = QIcon() 621 | icon.addPixmap(QPixmap(os.path.join(os.path.dirname(__file__), "loading.png")), QIcon.Normal, QIcon.Off) 622 | self.pushButton_update.setIcon(icon) 623 | self.addTopDirs() 624 | self.treeWidget_tree.expandAll() 625 | main.updateStatus = False 626 | #self.pushButton_update.setText("更新") 627 | 628 | def setButtonIcon(self): 629 | if self.movie: 630 | self.pushButton_update.setIcon(QIcon(self.movie.currentPixmap())) 631 | 632 | def setUpdateStatus(self): 633 | self.movie = QMovie(os.path.join(os.path.dirname(__file__), "loading.gif"), QByteArray(), self) 634 | self.movie.frameChanged.connect(self.setButtonIcon) 635 | self.movie.start() 636 | #self.pushButton_update.setText("更新中") 637 | 638 | def addTopDirs(self): 639 | self.treeWidget_tree.clear() 640 | files = os.listdir(main.gitNoteNoteHome) 641 | for eachone in files: 642 | eachone_d = os.path.join(main.gitNoteNoteHome, eachone) 643 | if os.path.isdir(eachone_d) and eachone[0] != '.': 644 | item = QTreeWidgetItem(self.treeWidget_tree) 645 | countMd = self.countMdFiles(eachone_d) 646 | dirName = eachone 647 | if countMd > 0: 648 | dirName = eachone + " (" + str(countMd) + ")" 649 | item.setText(0, dirName) 650 | item.setText(1, eachone_d) 651 | item.setIcon(0, self.dirIcon) 652 | self.showDirs(eachone_d, item) 653 | self.treeWidget_tree.expandAll() 654 | 655 | def showDirs(self, filepath, item): 656 | files = os.listdir(filepath) 657 | for eachone in files: 658 | eachone_d = os.path.join(filepath, eachone) 659 | if os.path.isdir(eachone_d) and eachone[0] != '.': 660 | childitem = QTreeWidgetItem(item) 661 | countMd = self.countMdFiles(eachone_d) 662 | dirName = eachone 663 | if countMd > 0: 664 | dirName = eachone + " (" + str(countMd) + ")" 665 | childitem.setText(0, dirName) 666 | childitem.setText(1, eachone_d) 667 | childitem.setIcon(0, self.dirIcon) 668 | self.showDirs(eachone_d, childitem) 669 | 670 | def updateTreeItemName(self): 671 | baseName = self.treeItem.text(0).split('(')[0] 672 | countMd = self.countMdFiles(self.treeItem.text(1)) 673 | dirName = baseName 674 | if countMd > 0: 675 | dirName = dirName + " (" + str(countMd) + ")" 676 | self.treeItem.setText(0, dirName) 677 | 678 | def countMdFiles(self, filepath): 679 | files = os.listdir(filepath) 680 | number = 0 681 | for eachone in files: 682 | eachone_d = os.path.join(filepath, eachone) 683 | if not os.path.isdir(eachone_d) and eachone_d[-3:] == ".md": 684 | number = number + 1 685 | return number 686 | 687 | def onTreeClicked(self, qmodelindex): 688 | self.treeItem = self.treeWidget_tree.currentItem() 689 | if self.treeItem.text(1) != self.listfileDir: 690 | self.listfileDir = self.treeItem.text(1) 691 | self.updateListView(self.listfileDir) 692 | 693 | def updateListView(self, fileDir): 694 | self.listWidget_list.clear() 695 | if fileDir == main.gitNoteNoteHome: 696 | return 697 | self.fileList = self.traverseDir(fileDir) 698 | for eachone in self.fileList: 699 | tmpitem = QListWidgetItem(eachone[0]) 700 | #if len(eachone[2]) > 1: 701 | # tmpitem.setIcon(QIcon(eachone[2])) 702 | #tmpitem.setTextAlignment(Qt.AlignLeft) 703 | #tmpitem.setSizeHint(QSize(150, 100)) 704 | self.listWidget_list.addItem(tmpitem) 705 | 706 | def traverseDir(self, filepath): 707 | files = os.listdir(filepath) 708 | fileList = [] 709 | for eachone in files: 710 | eachone_d = os.path.join(filepath, eachone) 711 | if not os.path.isdir(eachone_d) and eachone_d[-3:] == ".md": 712 | tmpfileinfo = [] 713 | tmpfileinfo.append(eachone[:-3] + "\n" + self.read20words(eachone_d)) 714 | tmpfileinfo.append(os.path.getmtime(eachone_d)) 715 | #tmpfileinfo.append(self.addPictureToList(eachone_d)) 716 | fileList.append(tmpfileinfo) 717 | if len(fileList) > 1: 718 | fileList.sort(key=operator.itemgetter(1), reverse=True) 719 | return fileList 720 | 721 | def addPictureToList(self, filepath): 722 | returnStr = " " 723 | readHandle = open(filepath, "r", encoding='UTF-8') 724 | while True: 725 | oneline = readHandle.readline() 726 | if oneline: 727 | if "![](" in oneline and ")" in oneline.split("![](")[1]: 728 | shotfile = (oneline.split("![](")[1]).split(")")[0] 729 | realfile = os.path.join(self.listfileDir, shotfile) 730 | if os.path.exists(realfile): 731 | returnStr = realfile 732 | else: 733 | break 734 | readHandle.close() 735 | return returnStr 736 | 737 | def read20words(self, filepath): 738 | returnStr = "" 739 | readHandle = open(filepath, "r", encoding='UTF-8') 740 | while True: 741 | oneline = readHandle.readline() 742 | returnStr = returnStr + oneline.strip() 743 | if oneline and len(returnStr) <20: 744 | oneline = readHandle.readline() 745 | returnStr = returnStr + oneline.strip() 746 | else: 747 | break 748 | readHandle.close() 749 | if len(returnStr) > 10: 750 | returnStr = returnStr[:10] + "\n" + returnStr[10:] 751 | returnStr = returnStr + "\n" + self.timeStampToTime(os.path.getmtime(filepath)) 752 | return returnStr 753 | 754 | def timeStampToTime(self, timestamp): 755 | timeStruct = time.localtime(timestamp) 756 | return time.strftime("%Y-%m-%d %H:%M:%S", timeStruct) 757 | -------------------------------------------------------------------------------- /GitNote.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Form_note 4 | 5 | 6 | 7 | 0 8 | 0 9 | 1105 10 | 789 11 | 12 | 13 | 14 | 15 | 0 16 | 0 17 | 18 | 19 | 20 | 笔记 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 16 33 | 16 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | Qt::Horizontal 42 | 43 | 44 | QSizePolicy::Minimum 45 | 46 | 47 | 48 | 0 49 | 0 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | Qt::Horizontal 67 | 68 | 69 | false 70 | 71 | 72 | 73 | Qt::Horizontal 74 | 75 | 76 | false 77 | 78 | 79 | 80 | 81 | 10 82 | 0 83 | 84 | 85 | 86 | 87 | 200 88 | 16777215 89 | 90 | 91 | 92 | 93 | 0 94 | 0 95 | 96 | 97 | 98 | 99 | 0 100 | 0 101 | 102 | 103 | 104 | Qt::CustomContextMenu 105 | 106 | 107 | false 108 | 109 | 110 | 111 | 1 112 | 113 | 114 | 115 | 116 | 117 | 118 | 10 119 | 0 120 | 121 | 122 | 123 | 124 | 150 125 | 16777215 126 | 127 | 128 | 129 | Qt::CustomContextMenu 130 | 131 | 132 | 133 | 0 134 | 0 135 | 136 | 137 | 138 | Qt::ElideMiddle 139 | 140 | 141 | QListView::LeftToRight 142 | 143 | 144 | true 145 | 146 | 147 | QListView::Adjust 148 | 149 | 150 | 5 151 | 152 | 153 | QListView::ListMode 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 20 169 | 20 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | true 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 20 189 | 20 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 20 202 | 20 203 | 204 | 205 | 206 | QToolButton::MenuButtonPopup 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | true 218 | 219 | 220 | QAbstractScrollArea::AdjustToContents 221 | 222 | 223 | 224 | 225 | 226 | 227 | true 228 | 229 | 230 | true 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | treeWidget_tree 244 | listWidget_list 245 | lineEdit_title 246 | plainTextEdit_markdown 247 | textEdit_show 248 | pushButton_update 249 | pushButton_save 250 | 251 | 252 | 253 | 254 | -------------------------------------------------------------------------------- /GitNoteUi.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file 'GitNote.ui' 4 | # 5 | # Created by: PyQt5 UI code generator 5.12.1 6 | # 7 | # WARNING! All changes made in this file will be lost! 8 | 9 | from PyQt5 import QtCore, QtGui, QtWidgets 10 | 11 | 12 | class Ui_Form_note(object): 13 | def setupUi(self, Form_note): 14 | Form_note.setObjectName("Form_note") 15 | Form_note.resize(1105, 789) 16 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) 17 | sizePolicy.setHorizontalStretch(0) 18 | sizePolicy.setVerticalStretch(0) 19 | sizePolicy.setHeightForWidth(Form_note.sizePolicy().hasHeightForWidth()) 20 | Form_note.setSizePolicy(sizePolicy) 21 | self.verticalLayout_2 = QtWidgets.QVBoxLayout(Form_note) 22 | self.verticalLayout_2.setObjectName("verticalLayout_2") 23 | self.horizontalLayout = QtWidgets.QHBoxLayout() 24 | self.horizontalLayout.setObjectName("horizontalLayout") 25 | self.pushButton_update = QtWidgets.QPushButton(Form_note) 26 | self.pushButton_update.setText("") 27 | self.pushButton_update.setIconSize(QtCore.QSize(16, 16)) 28 | self.pushButton_update.setObjectName("pushButton_update") 29 | self.horizontalLayout.addWidget(self.pushButton_update) 30 | spacerItem = QtWidgets.QSpacerItem(0, 0, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) 31 | self.horizontalLayout.addItem(spacerItem) 32 | self.toolButton_config = QtWidgets.QToolButton(Form_note) 33 | self.toolButton_config.setText("") 34 | self.toolButton_config.setObjectName("toolButton_config") 35 | self.horizontalLayout.addWidget(self.toolButton_config) 36 | self.horizontalLayout.setStretch(1, 1) 37 | self.verticalLayout_2.addLayout(self.horizontalLayout) 38 | self.splitter_2 = QtWidgets.QSplitter(Form_note) 39 | self.splitter_2.setOrientation(QtCore.Qt.Horizontal) 40 | self.splitter_2.setChildrenCollapsible(False) 41 | self.splitter_2.setObjectName("splitter_2") 42 | self.splitter = QtWidgets.QSplitter(self.splitter_2) 43 | self.splitter.setOrientation(QtCore.Qt.Horizontal) 44 | self.splitter.setChildrenCollapsible(False) 45 | self.splitter.setObjectName("splitter") 46 | self.treeWidget_tree = QtWidgets.QTreeWidget(self.splitter) 47 | self.treeWidget_tree.setMinimumSize(QtCore.QSize(10, 0)) 48 | self.treeWidget_tree.setMaximumSize(QtCore.QSize(200, 16777215)) 49 | self.treeWidget_tree.setSizeIncrement(QtCore.QSize(0, 0)) 50 | self.treeWidget_tree.setBaseSize(QtCore.QSize(0, 0)) 51 | self.treeWidget_tree.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) 52 | self.treeWidget_tree.setObjectName("treeWidget_tree") 53 | self.treeWidget_tree.headerItem().setText(0, "1") 54 | self.treeWidget_tree.header().setVisible(False) 55 | self.listWidget_list = QtWidgets.QListWidget(self.splitter) 56 | self.listWidget_list.setMinimumSize(QtCore.QSize(10, 0)) 57 | self.listWidget_list.setMaximumSize(QtCore.QSize(150, 16777215)) 58 | self.listWidget_list.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) 59 | self.listWidget_list.setIconSize(QtCore.QSize(0, 0)) 60 | self.listWidget_list.setTextElideMode(QtCore.Qt.ElideMiddle) 61 | self.listWidget_list.setFlow(QtWidgets.QListView.LeftToRight) 62 | self.listWidget_list.setProperty("isWrapping", True) 63 | self.listWidget_list.setResizeMode(QtWidgets.QListView.Adjust) 64 | self.listWidget_list.setViewMode(QtWidgets.QListView.ListMode) 65 | self.listWidget_list.setObjectName("listWidget_list") 66 | self.layoutWidget = QtWidgets.QWidget(self.splitter_2) 67 | self.layoutWidget.setObjectName("layoutWidget") 68 | self.verticalLayout = QtWidgets.QVBoxLayout(self.layoutWidget) 69 | self.verticalLayout.setContentsMargins(0, 0, 0, 0) 70 | self.verticalLayout.setObjectName("verticalLayout") 71 | self.horizontalLayout_3 = QtWidgets.QHBoxLayout() 72 | self.horizontalLayout_3.setObjectName("horizontalLayout_3") 73 | self.pushButton_save = QtWidgets.QPushButton(self.layoutWidget) 74 | self.pushButton_save.setText("") 75 | self.pushButton_save.setIconSize(QtCore.QSize(20, 20)) 76 | self.pushButton_save.setObjectName("pushButton_save") 77 | self.horizontalLayout_3.addWidget(self.pushButton_save) 78 | self.lineEdit_title = QtWidgets.QLineEdit(self.layoutWidget) 79 | self.lineEdit_title.setReadOnly(True) 80 | self.lineEdit_title.setObjectName("lineEdit_title") 81 | self.horizontalLayout_3.addWidget(self.lineEdit_title) 82 | self.pushButton_addpicture = QtWidgets.QPushButton(self.layoutWidget) 83 | self.pushButton_addpicture.setText("") 84 | self.pushButton_addpicture.setIconSize(QtCore.QSize(20, 20)) 85 | self.pushButton_addpicture.setObjectName("pushButton_addpicture") 86 | self.horizontalLayout_3.addWidget(self.pushButton_addpicture) 87 | self.toolButton_functions = QtWidgets.QToolButton(self.layoutWidget) 88 | self.toolButton_functions.setText("") 89 | self.toolButton_functions.setIconSize(QtCore.QSize(20, 20)) 90 | self.toolButton_functions.setPopupMode(QtWidgets.QToolButton.MenuButtonPopup) 91 | self.toolButton_functions.setObjectName("toolButton_functions") 92 | self.horizontalLayout_3.addWidget(self.toolButton_functions) 93 | self.verticalLayout.addLayout(self.horizontalLayout_3) 94 | self.horizontalLayout_2 = QtWidgets.QHBoxLayout() 95 | self.horizontalLayout_2.setObjectName("horizontalLayout_2") 96 | self.plainTextEdit_markdown = QtWidgets.QPlainTextEdit(self.layoutWidget) 97 | self.plainTextEdit_markdown.setEnabled(True) 98 | self.plainTextEdit_markdown.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents) 99 | self.plainTextEdit_markdown.setObjectName("plainTextEdit_markdown") 100 | self.horizontalLayout_2.addWidget(self.plainTextEdit_markdown) 101 | self.textEdit_show = QtWidgets.QTextEdit(self.layoutWidget) 102 | self.textEdit_show.setReadOnly(True) 103 | self.textEdit_show.setOverwriteMode(True) 104 | self.textEdit_show.setObjectName("textEdit_show") 105 | self.horizontalLayout_2.addWidget(self.textEdit_show) 106 | self.verticalLayout.addLayout(self.horizontalLayout_2) 107 | self.verticalLayout_2.addWidget(self.splitter_2) 108 | 109 | self.retranslateUi(Form_note) 110 | QtCore.QMetaObject.connectSlotsByName(Form_note) 111 | Form_note.setTabOrder(self.treeWidget_tree, self.listWidget_list) 112 | Form_note.setTabOrder(self.listWidget_list, self.lineEdit_title) 113 | Form_note.setTabOrder(self.lineEdit_title, self.plainTextEdit_markdown) 114 | Form_note.setTabOrder(self.plainTextEdit_markdown, self.textEdit_show) 115 | Form_note.setTabOrder(self.textEdit_show, self.pushButton_update) 116 | Form_note.setTabOrder(self.pushButton_update, self.pushButton_save) 117 | 118 | def retranslateUi(self, Form_note): 119 | _translate = QtCore.QCoreApplication.translate 120 | Form_note.setWindowTitle(_translate("Form_note", "笔记")) 121 | 122 | 123 | -------------------------------------------------------------------------------- /GitNote_back.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Form_note 4 | 5 | 6 | 7 | 0 8 | 0 9 | 1105 10 | 779 11 | 12 | 13 | 14 | 笔记 15 | 16 | 17 | 18 | app.icoapp.ico 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 更新 27 | 28 | 29 | 30 | 31 | 32 | 33 | 新建 34 | 35 | 36 | 37 | 38 | 39 | 40 | 编辑 41 | 42 | 43 | 44 | 45 | 46 | 47 | Qt::Horizontal 48 | 49 | 50 | QSizePolicy::Minimum 51 | 52 | 53 | 54 | 0 55 | 0 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | Qt::Horizontal 66 | 67 | 68 | false 69 | 70 | 71 | 1 72 | 73 | 74 | false 75 | 76 | 77 | 78 | 79 | 100 80 | 0 81 | 82 | 83 | 84 | 85 | 200 86 | 16777215 87 | 88 | 89 | 90 | 91 | 250 92 | 0 93 | 94 | 95 | 96 | 97 | 250 98 | 0 99 | 100 | 101 | 102 | false 103 | 104 | 105 | 106 | 1 107 | 108 | 109 | 110 | 111 | 112 | 113 | 100 114 | 0 115 | 116 | 117 | 118 | 119 | 150 120 | 16777215 121 | 122 | 123 | 124 | true 125 | 126 | 127 | QListView::Adjust 128 | 129 | 130 | 10 131 | 132 | 133 | 134 | 135 | true 136 | 137 | 138 | 139 | 140 | true 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | -------------------------------------------------------------------------------- /Init.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import sys, os, getpass 5 | import LogOn 6 | import main, FileAll, GitNote 7 | import pathlib 8 | 9 | class Init: 10 | def __init__(self): 11 | self.exist = True 12 | main.gitNoteHome = os.path.join(str(pathlib.Path.home()), ".GitNote") 13 | main.gitNoteNoteHome = os.path.join(main.gitNoteHome, "Notes") 14 | if not os.path.exists(main.gitNoteHome): 15 | self.exist = False 16 | os.makedirs(main.gitNoteHome) 17 | os.makedirs(main.gitNoteNoteHome) 18 | print("No .GitNOte") 19 | if not os.path.exists(main.gitNoteNoteHome): 20 | main.gitExist = False 21 | os.makedirs(main.gitNoteNoteHome) 22 | print("No Notes") 23 | if not os.path.exists(os.path.join(main.gitNoteHome, "config")): 24 | self.exist = False 25 | print("NO config:" + os.path.join(main.gitNoteHome, "config")) 26 | if not os.path.exists(os.path.join(main.gitNoteNoteHome, ".git")): 27 | main.gitExist = False 28 | if not self.exist: 29 | self.logWin = LogOn.LogOn() 30 | else: 31 | FileAll.readConfig() 32 | main.setGitEnv() 33 | main.myGitNote = GitNote.GitNote() -------------------------------------------------------------------------------- /LogOn.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | 4 | from PyQt5.QtWidgets import QWidget, QApplication, QMessageBox, QLineEdit 5 | from LogOnUi import * 6 | import main, FileAll, GitNote 7 | import os, git 8 | 9 | class LogOn(QWidget, Ui_LogOn): 10 | def __init__(self, parent=None): 11 | super(LogOn, self).__init__(parent) 12 | self.setupUi(self) 13 | self.initUi() 14 | self.show() 15 | 16 | def initUi(self): 17 | self.lineEdit_password.setEchoMode(QLineEdit.Password) 18 | self.pushButton_logOn.clicked.connect(self.logOn) 19 | 20 | def logOn(self): 21 | if len(self.lineEdit_userName.text()) < 2: 22 | QMessageBox.information(self, "警告", "请输入有效的用户名", QMessageBox.Ok) 23 | return 24 | if len(self.lineEdit_password.text()) < 2: 25 | QMessageBox.information(self, "警告", "请输入有效的密码", QMessageBox.Ok) 26 | return 27 | if (self.lineEdit_gitUrl.text())[-3:] != "git": 28 | QMessageBox.information(self, "警告", "请输入有效的Git地址", QMessageBox.Ok) 29 | return 30 | main.userName = self.lineEdit_userName.text() 31 | main.password = self.lineEdit_password.text() 32 | main.gitUrl = self.lineEdit_gitUrl.text() 33 | FileAll.storeConfig() 34 | self.close() 35 | main.setGitEnv() 36 | main.myGitNote = GitNote.GitNote() -------------------------------------------------------------------------------- /LogOn.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | LogOn 4 | 5 | 6 | 7 | 0 8 | 0 9 | 388 10 | 150 11 | 12 | 13 | 14 | 登录 15 | 16 | 17 | 18 | 19 | 20 | 用户名: 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 密码: 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | Git地址: 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 登录 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /LogOnUi.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file 'LogOn.ui' 4 | # 5 | # Created by: PyQt5 UI code generator 5.12.1 6 | # 7 | # WARNING! All changes made in this file will be lost! 8 | 9 | from PyQt5 import QtCore, QtGui, QtWidgets 10 | 11 | 12 | class Ui_LogOn(object): 13 | def setupUi(self, LogOn): 14 | LogOn.setObjectName("LogOn") 15 | LogOn.resize(388, 150) 16 | self.formLayout = QtWidgets.QFormLayout(LogOn) 17 | self.formLayout.setObjectName("formLayout") 18 | self.label = QtWidgets.QLabel(LogOn) 19 | self.label.setObjectName("label") 20 | self.formLayout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label) 21 | self.lineEdit_userName = QtWidgets.QLineEdit(LogOn) 22 | self.lineEdit_userName.setObjectName("lineEdit_userName") 23 | self.formLayout.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.lineEdit_userName) 24 | self.label_2 = QtWidgets.QLabel(LogOn) 25 | self.label_2.setObjectName("label_2") 26 | self.formLayout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.label_2) 27 | self.lineEdit_password = QtWidgets.QLineEdit(LogOn) 28 | self.lineEdit_password.setObjectName("lineEdit_password") 29 | self.formLayout.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.lineEdit_password) 30 | self.label_3 = QtWidgets.QLabel(LogOn) 31 | self.label_3.setObjectName("label_3") 32 | self.formLayout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.label_3) 33 | self.lineEdit_gitUrl = QtWidgets.QLineEdit(LogOn) 34 | self.lineEdit_gitUrl.setObjectName("lineEdit_gitUrl") 35 | self.formLayout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.lineEdit_gitUrl) 36 | self.pushButton_logOn = QtWidgets.QPushButton(LogOn) 37 | self.pushButton_logOn.setObjectName("pushButton_logOn") 38 | self.formLayout.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.pushButton_logOn) 39 | 40 | self.retranslateUi(LogOn) 41 | QtCore.QMetaObject.connectSlotsByName(LogOn) 42 | 43 | def retranslateUi(self, LogOn): 44 | _translate = QtCore.QCoreApplication.translate 45 | LogOn.setWindowTitle(_translate("LogOn", "登录")) 46 | self.label.setText(_translate("LogOn", "用户名:")) 47 | self.label_2.setText(_translate("LogOn", "密码:")) 48 | self.label_3.setText(_translate("LogOn", "Git地址:")) 49 | self.pushButton_logOn.setText(_translate("LogOn", "登录")) 50 | 51 | 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GitNote 2 | --------------------------------------- 3 | 使用Git作为存储仓库的MarkDown云笔记。 4 | 5 | 安装必要环境,安装并配置Git 6 | 7 | 如果需要生成pdf,需要安装wkhtmltopdf 8 | 9 | ``` 10 | sudo apt install wkhtmltopdf 11 | 或者 12 | yum install wkhtmltopdf 13 | Windows下直接下载安装文件安装(https://wkhtmltopdf.org/downloads.html),将可执行文件的路径加入系统环境变量path中 14 | ``` 15 | 16 | ``` 17 | sudo apt install python3-pip 18 | pip3 install pyqt5 gitpython mistune pdfkit 19 | ``` 20 | 21 | ### 现在已完成基本功能,包括 22 | - 克隆 23 | - 更新,包括自动pull和push 24 | - markdown查看和编辑 25 | - 另存为pdf文件 26 | 27 | ### 其他功能开发中... 28 | 29 | 以下是一些现有功能截图 30 | ![](pictures/gitnote-11.png) 31 | 32 | ![](pictures/gitnote-12.png) 33 | -------------------------------------------------------------------------------- /__pycache__/FileAll.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluohust/GitNote/8a32a5463b2b670130e4d475e035cf1d1809625e/__pycache__/FileAll.cpython-36.pyc -------------------------------------------------------------------------------- /__pycache__/GitNote.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluohust/GitNote/8a32a5463b2b670130e4d475e035cf1d1809625e/__pycache__/GitNote.cpython-36.pyc -------------------------------------------------------------------------------- /__pycache__/GitNoteUi.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluohust/GitNote/8a32a5463b2b670130e4d475e035cf1d1809625e/__pycache__/GitNoteUi.cpython-36.pyc -------------------------------------------------------------------------------- /__pycache__/Init.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluohust/GitNote/8a32a5463b2b670130e4d475e035cf1d1809625e/__pycache__/Init.cpython-36.pyc -------------------------------------------------------------------------------- /__pycache__/LogOn.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluohust/GitNote/8a32a5463b2b670130e4d475e035cf1d1809625e/__pycache__/LogOn.cpython-36.pyc -------------------------------------------------------------------------------- /__pycache__/LogOnUi.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluohust/GitNote/8a32a5463b2b670130e4d475e035cf1d1809625e/__pycache__/LogOnUi.cpython-36.pyc -------------------------------------------------------------------------------- /__pycache__/main.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluohust/GitNote/8a32a5463b2b670130e4d475e035cf1d1809625e/__pycache__/main.cpython-36.pyc -------------------------------------------------------------------------------- /addpicture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluohust/GitNote/8a32a5463b2b670130e4d475e035cf1d1809625e/addpicture.png -------------------------------------------------------------------------------- /app.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluohust/GitNote/8a32a5463b2b670130e4d475e035cf1d1809625e/app.ico -------------------------------------------------------------------------------- /askpass.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Short & sweet script for use with git clone and fetch credentials. 4 | # Requires GIT_USERNAME and GIT_PASSWORD environment variables, 5 | # intended to be called by Git via GIT_ASKPASS. 6 | # 7 | 8 | from sys import argv 9 | from os import environ 10 | 11 | if 'username' in argv[1].lower(): 12 | print(environ['GIT_USERNAME']) 13 | exit() 14 | 15 | if 'password' in argv[1].lower(): 16 | print(environ['GIT_PASSWORD']) 17 | exit() 18 | 19 | exit(1) -------------------------------------------------------------------------------- /blackdir.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluohust/GitNote/8a32a5463b2b670130e4d475e035cf1d1809625e/blackdir.ico -------------------------------------------------------------------------------- /config.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluohust/GitNote/8a32a5463b2b670130e4d475e035cf1d1809625e/config.ico -------------------------------------------------------------------------------- /convert.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluohust/GitNote/8a32a5463b2b670130e4d475e035cf1d1809625e/convert.ico -------------------------------------------------------------------------------- /dir.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluohust/GitNote/8a32a5463b2b670130e4d475e035cf1d1809625e/dir.ico -------------------------------------------------------------------------------- /dir_back.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluohust/GitNote/8a32a5463b2b670130e4d475e035cf1d1809625e/dir_back.ico -------------------------------------------------------------------------------- /edit.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluohust/GitNote/8a32a5463b2b670130e4d475e035cf1d1809625e/edit.ico -------------------------------------------------------------------------------- /loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluohust/GitNote/8a32a5463b2b670130e4d475e035cf1d1809625e/loading.gif -------------------------------------------------------------------------------- /loading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluohust/GitNote/8a32a5463b2b670130e4d475e035cf1d1809625e/loading.png -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import sys, os 5 | import git 6 | from PyQt5.QtWidgets import QApplication 7 | import Init, GitNote 8 | 9 | gitNoteHome = "" 10 | gitNoteNoteHome = "" 11 | 12 | userName = "" 13 | password = "" 14 | gitUrl = "" 15 | 16 | gitExist = True 17 | 18 | myGitNote = "" 19 | updateStatus = False 20 | 21 | def setGitEnv(): 22 | os.environ['GIT_ASKPASS'] = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'askpass.py') 23 | os.environ['GIT_USERNAME'] = userName 24 | os.environ['GIT_PASSWORD'] = password 25 | 26 | if __name__ == '__main__': 27 | app = QApplication(sys.argv) 28 | myInit = Init.Init() 29 | sys.exit(app.exec_()) -------------------------------------------------------------------------------- /pictures/gitnote-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluohust/GitNote/8a32a5463b2b670130e4d475e035cf1d1809625e/pictures/gitnote-1.png -------------------------------------------------------------------------------- /pictures/gitnote-10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluohust/GitNote/8a32a5463b2b670130e4d475e035cf1d1809625e/pictures/gitnote-10.png -------------------------------------------------------------------------------- /pictures/gitnote-11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluohust/GitNote/8a32a5463b2b670130e4d475e035cf1d1809625e/pictures/gitnote-11.png -------------------------------------------------------------------------------- /pictures/gitnote-12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluohust/GitNote/8a32a5463b2b670130e4d475e035cf1d1809625e/pictures/gitnote-12.png -------------------------------------------------------------------------------- /pictures/gitnote-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluohust/GitNote/8a32a5463b2b670130e4d475e035cf1d1809625e/pictures/gitnote-2.png -------------------------------------------------------------------------------- /pictures/gitnote-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluohust/GitNote/8a32a5463b2b670130e4d475e035cf1d1809625e/pictures/gitnote-3.png -------------------------------------------------------------------------------- /pictures/gitnote-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluohust/GitNote/8a32a5463b2b670130e4d475e035cf1d1809625e/pictures/gitnote-4.png -------------------------------------------------------------------------------- /pictures/gitnote-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluohust/GitNote/8a32a5463b2b670130e4d475e035cf1d1809625e/pictures/gitnote-5.png -------------------------------------------------------------------------------- /pictures/gitnote-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluohust/GitNote/8a32a5463b2b670130e4d475e035cf1d1809625e/pictures/gitnote-6.png -------------------------------------------------------------------------------- /pictures/gitnote-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluohust/GitNote/8a32a5463b2b670130e4d475e035cf1d1809625e/pictures/gitnote-7.png -------------------------------------------------------------------------------- /pictures/gitnote-8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluohust/GitNote/8a32a5463b2b670130e4d475e035cf1d1809625e/pictures/gitnote-8.png -------------------------------------------------------------------------------- /pictures/gitnote-9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluohust/GitNote/8a32a5463b2b670130e4d475e035cf1d1809625e/pictures/gitnote-9.png -------------------------------------------------------------------------------- /save.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pluohust/GitNote/8a32a5463b2b670130e4d475e035cf1d1809625e/save.ico --------------------------------------------------------------------------------