用python实现桌面备忘录

最近用windows10企业版的时候发现了一个问题,这上面的备忘录功能竟然不能打开,实在是难受,正好没事干,干脆用python写一个吧......正好初学python,也就当个练习了。要准备的工具呢就是python解释器和pyqt5了。如果需要打包在其他电脑上使用呢,可以安装pyinstaller来进行打包。话不多说,先上效果图。

图片.png
图片.png

UI设计就算了,以实用为准,毕竟没有审美。里面还剩余一下可以改进的地方,也懒得改了(毕竟几个小时弄出来的东西肯定还是有许多bug的),感兴趣的朋友可以自己改一下。

首先呢,我们来自定义一下这个标题栏,系统自带的属实有点丑。这里参考了晓虎虎在此
先生的(https://blog.csdn.net/qq_37386287/article/details/87900403)这篇博客以及Unlucky
先生的(https://www.jianshu.com/p/1b91fd2beb85)这篇博客,感谢!!!

from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import sys
import sqlite3

# 按钮高度
BUTTON_HEIGHT = 30
# 按钮宽度
BUTTON_WIDTH = 30
# 标题栏高度
TITLE_HEIGHT = 30
class TitleWidget(QWidget):
    def __init__(self):
        super().__init__()
        # self.setStyleSheet("background-color:blue")
        titleIcon = QPixmap("image/icon.png")
        self.Icon = myLabel()
        self.Icon.setPixmap(titleIcon.scaled(20, 20))
        titleContent = QLabel("备忘录")
        titleContent.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
        titleContent.setFixedHeight(TITLE_HEIGHT)
        titleContent.setObjectName("TitleContent")
        self.ButtonMin = QPushButton()
        self.ButtonMin.setFixedSize(QSize(BUTTON_WIDTH, BUTTON_HEIGHT))
        self.ButtonMin.setObjectName("ButtonMin")
        self.ButtonMax = QPushButton()
        self.ButtonMax.setFixedSize(QSize(BUTTON_WIDTH, BUTTON_HEIGHT))
        self.ButtonMax.setObjectName("ButtonMax")
        self.ButtonRestore = QPushButton()
        self.ButtonRestore.setFixedSize(QSize(BUTTON_WIDTH, BUTTON_HEIGHT))
        self.ButtonRestore.setObjectName("ButtonRestore")
        self.ButtonRestore.setVisible(False)
        self.ButtonClose = QPushButton()
        self.ButtonClose.setFixedSize(QSize(BUTTON_WIDTH, BUTTON_HEIGHT))
        self.ButtonClose.setObjectName("ButtonClose")
        mylayout = QHBoxLayout()
        mylayout.setSpacing(0)
        mylayout.setContentsMargins(5, 5, 5, 5)
        mylayout.addWidget(self.Icon)

        mylayout.addWidget(titleContent)
        mylayout.addWidget(self.ButtonMin)
        mylayout.addWidget(self.ButtonMax)
        mylayout.addWidget(self.ButtonRestore)
        mylayout.addWidget(self.ButtonClose)

        self.setLayout(mylayout)
        # QSS可写在文件中 读文件使用 这里方便大家使用直接写在代码里吧
        Qss = '''

            QLabel#TitleContent
            {
                color: #FFFFFF;
            }

            QPushButton#ButtonMin
            {
                border-image:url(image/min.png) 0 81 0 0 ;

            }

            QPushButton#ButtonMin:hover
            {
                border-image:url(image/min.png) 0 54 0 27 ;
            }

            QPushButton#ButtonMin:pressed
            {
                border-image:url(image/min.png) 0 27 0 54 ;
            }

            QPushButton#ButtonMax
            {
                border-image:url(image/max.png) 0 81 0 0 ;
            }

            QPushButton#ButtonMax:hover
            {
                border-image:url(image/max.png) 0 54 0 27 ;
            }

            QPushButton#ButtonMax:pressed
            {
                border-image:url(image/max.png) 0 27 0 54 ;
            }

            QPushButton#ButtonRestore
            {
                border-image:url(image/restore.png) 0 81 0 0 ;
            }

            QPushButton#ButtonRestore:hover
            {
                border-image:url(image/restore.png) 0 54 0 27 ;
            }

            QPushButton#ButtonRestore:pressed
            {
                border-image:url(image/restore.png) 0 27 0 54 ;
            }

            QPushButton#ButtonClose
            {
                border-image:url(image/close.png) 0 81 0 0 ;
                border-top-right-radius:3 ;
            }

            QPushButton#ButtonClose:hover
            {
                border-image:url(image/close.png) 0 54 0 27 ;
                border-top-right-radius:3 ;
            }

            QPushButton#ButtonClose:pressed
            {
                border-image:url(image/close.png) 0 27 0 54 ;
                border-top-right-radius:3 ;
            }

        '''
        self.setStyleSheet(Qss)

        self.restorePos = None
        self.restoreSize = None
        self.startMovePos = None

    def saveRestoreInfo(self, point, size):
        self.restorePos = point
        self.restoreSize = size

    def getRestoreInfo(self):
        return self.restorePos, self.restoreSize

然后我这里呢由于是点击备忘录三个字左边的应用图标来进行新增记录的操作,而根据上面的代码呢,我是用QLabel来放的这个图片。QLabel是没有点击操作的,所以我们需要自己定义一个QLabel来重写OnClick方法。如下。

class myLabel(QLabel):
    _signal = pyqtSignal(str)

    def __init__(self, parent=None):
        super(myLabel, self).__init__(parent)

    def mousePressEvent(self, e):  ##重载一下鼠标点击事件
        self._signal.emit("")

此外,还需要什么东西呢?是不是需要一个添加新记录的窗口,这个窗口呢,我们也来自定义一下。也就是我最开始上的图里面的第二张。

class InputDialog(QWidget):
    def __init__(self):
        super(InputDialog, self).__init__()
        self.initUi()
        self.id = 0
        self.show()

    def load_data(self, id, time, event, level):
        self.time.setText(time)
        self.event.setText(event)
        self.level.setText(level)
        self.id = id

    def initUi(self):
        self.setWindowTitle("新增/修改记录")
        titleIcon = QIcon("image/icon_black.png")
        self.setWindowIcon(titleIcon)
        self.resize(300, 200)
        self.center()
        label1 = QLabel("时    间:")
        label2 = QLabel("事    件:")
        label3 = QLabel("紧急程度:")

        self.time = QLineEdit()
        self.event = QLineEdit()
        self.level = QLineEdit()
        self.level.setPlaceholderText("输入数字,越大越紧急")
        self.ok_btn = QPushButton("确定")

        self.cancel_btn = QPushButton("取消")
        mainLayout = QVBoxLayout()
        topWidget = QWidget()
        botWidget = QWidget()
        btnLay = QHBoxLayout()
        glLay = QGridLayout()
        glLay.addWidget(label1, 0, 0)
        glLay.addWidget(self.time, 0, 1)
        glLay.addWidget(label2, 1, 0)
        glLay.addWidget(self.event, 1, 1)
        glLay.addWidget(label3, 2, 0)
        glLay.addWidget(self.level, 2, 1)
        btnLay.addWidget(self.ok_btn, 1)
        btnLay.addWidget(self.cancel_btn,1)
        topWidget.setLayout(glLay)
        botWidget.setLayout(btnLay)
        mainLayout.addWidget(topWidget)
        mainLayout.addWidget(botWidget)
        self.setLayout(mainLayout)

    # 控制窗口显示在屏幕中心的方法
    def center(self):
        # 获得窗口
        qr = self.frameGeometry()
        # 获得屏幕中心点
        cp = QDesktopWidget().availableGeometry().center()
        # 显示到屏幕中心
        qr.moveCenter(cp)
        self.move(qr.topLeft())

注意看这个InputDialog弹出窗口里面有个load_data方法,这个方法的用处呢,主要是在打开窗口的时候判断是新增记录还是修改记录的,如果是新增记录呢就不需要调用这个load_data方法,如果是修改记录呢那就需要传入之前的数据。

最后是我们的主程序,主程序考虑的是需要一个列表来对数据进行展示,本来想弄得对其一点好看一点的,但是对于这个QListView不是很了解,找到的许多方法都是只有简单的文字或者图片加文字,这里也用不到图片,索性就懒得弄了,直接纯文字,处理一下。至于数据存储呢,这里使用的是python自带的sqlite3数据库,轻量级,使用也很方便。

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowMinimizeButtonHint | Qt.WindowStaysOnTopHint)
        self.resize(350, 500)
        self.center()
        AllWidget = QWidget()
        # AllWidget.setStyleSheet("color:white;")
        Alllayout = QVBoxLayout()
        Alllayout.setSpacing(0)
        Alllayout.setContentsMargins(0, 0, 0, 0)
        AllWidget.setLayout(Alllayout)
        self.title = TitleWidget()
        self.title.setFixedWidth(self.width())
        self.title.setFixedHeight(TITLE_HEIGHT)
        self.setWindowOpacity(0.7)  # 设置窗口透明度
        # self.setAttribute(Qt.WA_TranslucentBackground)  # 设置窗口背景透明
        self.title.ButtonMin.clicked.connect(self.ButtonMinSlot)
        self.title.ButtonMax.clicked.connect(self.ButtonMaxSlot)
        self.title.ButtonRestore.clicked.connect(self.ButtonRestoreSlot)
        self.title.ButtonClose.clicked.connect(self.ButtonCloseSlot)
        self.title.Icon._signal.connect(self.ButtonAddNew)
        centerWidget = QWidget()
        ver_layout = QVBoxLayout(centerWidget)

        self.list_view = QListView()
        self.load_record()
        self.list_view.setContextMenuPolicy(3)
        self.list_view.customContextMenuRequested[QPoint].connect(self.listWidgetContext)
        self.list_view.doubleClicked.connect(self.clickedlist)  # listview 的点击事件
        self.list_view.setStyleSheet('''
            QListView{font-family:"微软雅黑"; text-size:16px;color:#005AB5; font-weight:bold;}
            QListView::item{margin-top:5px;margin-bottom:5px;}
            ''')
        self.list_view.setWordWrap(True)
        ver_layout.addWidget(self.list_view)
        # centerWidget 中可以随意添加自己想用的控件
        # centerWidget.setStyleSheet("background-color:red")
        self.Qss = '''
            QMainWindow{
                background:qlineargradient(spread:pad,x1:1,y1:0,x2:0,y2:0,stop:0 #3d3d3d,stop:1 #4d4d4d);  

            }
        '''

        Alllayout.addWidget(self.title)
        Alllayout.addWidget(centerWidget)
        self.setCentralWidget(AllWidget)
        self.setStyleSheet(self.Qss)
    def ButtonMinSlot(self):
        self.showMinimized()

    def ButtonMaxSlot(self):
        self.title.ButtonMax.setVisible(False)
        self.title.ButtonRestore.setVisible(True)
        self.title.saveRestoreInfo(self.pos(), QSize(self.width(), self.height()))
        desktopRect = QApplication.desktop().availableGeometry()
        FactRect = QRect(desktopRect.x() - 3, desktopRect.y() - 3, desktopRect.width() + 6, desktopRect.height() + 6)
        print(FactRect)
        self.setGeometry(FactRect)
        self.setFixedSize(desktopRect.width() + 6, desktopRect.height() + 6)

    def ButtonRestoreSlot(self):
        self.title.ButtonMax.setVisible(True)
        self.title.ButtonRestore.setVisible(False)
        windowPos, windowSize = self.title.getRestoreInfo()
        # print(windowPos,windowSize.width(),windowSize.height())
        self.setGeometry(windowPos.x(), windowPos.y(), windowSize.width(), windowSize.height())
        self.setFixedSize(windowSize.width(), windowSize.height())

    def ButtonCloseSlot(self):
        self.close()
    def paintEvent(self, event):
        self.title.setFixedWidth(self.width())

    def center(self):
        desktop = QApplication.desktop()
        self.move(desktop.width() - 400, int((desktop.height() - self.height()) / 2))

好,现在主程序的框架搭好了,我们还需要做什么呢?首先我们在打开软件的时候是不是需要主动去加载数据啊,所以接下来,我们需要搭建数据库的部分。

    def load_record(self):
        global data_source
        data_source = []
        conn = sqlite3.connect("memo.db")
        cursor = conn.cursor()
        # 执行一条语句,创建 user表
        sql = "create table if not exists record (id integer primary key autoincrement not null , memo_time varchar(30), " \
              "memo_event varchar(255), memo_level integer(5))"
        cursor.execute(sql)
        sql = "select * from record order by memo_level desc"
        result = cursor.execute(sql)
        slm = QStringListModel()  # 创建mode
        self.qList = []  # 添加的数组数据
        for row in result:
            data_source.append([str(row[0]),str(row[1]),str(row[2]),str(row[3])])
            self.qList.append("时间:" + str(row[1]) + "   事件:" + str(row[2]) + "   紧急程度:" + str(row[3]) + "级")
        if len(data_source) == 0:
            self.qList.append("还没有记录哦,点击左上角图标添加!")
        conn.commit()
        slm.setStringList(self.qList)  # 将数据设置到model
        self.list_view.setModel(slm)  ##绑定 listView 和 model
        cursor.close()
        conn.close()

load_record函数是一个主动访问数据库并加载数据的函数。首先使用conn = sqlite3.connect("memo.db")来连接数据库,这个函数会自动判断数据库是不是存在,不存在的话会自动创建。接下来需要创建表,创建表的时候需要判断一下是不是存在这个表,如果不存在则创建,存在的话就进行下一步。然后用result去接收数据库查询语句返回的结果,并在对这个结果进行迭代的时候将他放到我们的全局变量data_source中,方便后面的处理,打个比方,在其他函数中需要访问单条数据的内容时,我们怎么去拿到这个result的内容,result是不支持直接用数据的形式访问的,为了拿到这个数据的内容,我们不可能再去查询一遍数据库吧,这时候如果有一个全局变量可以支持数组的访问形式,那我们是不是就可以直接通过index来查找我们需要的数据。

好了,数据库的查询和可视化处理做好了,但是我们的数据表中还没有数据对不对,所以是时候完成添加备忘录的功能了。我这里呢把添加功能通过点击左上角的应用小图标来进行。在点击的时候弹出我们刚写好的input_dialog来进行输入。而主函数中的self.title.Icon._signal.connect(self.ButtonAddNew)则是对应用图标QLabel的信号插槽。链接到的函数如下。同时这里还将添加函数的功能分离成了三步(打开弹窗,数据预处理,数据插入),在点击图标时,先打开InputDialog,然后在dialog中的确定按钮被点击时需要先进行数据预处理操作self.dlg.ok_btn.clicked.connect(self.prepare_add_data),不能让用户插入不合格的数据,点击取消按钮时需要关闭弹窗。然后再进行数据库的插入操作 self.add_record(time, event, level),当然别忘了在插入数据后更新UI,我这里为了方便就直接调用了load_record函数。要注意的一点是QListView需要setWordWrap(True)这个属性,让里面的文字自动换行,不然会很丑。

    def ButtonAddNew(self):
        self.dlg = InputDialog()
        self.dlg.setStyleSheet(self.Qss)
        self.dlg.ok_btn.clicked.connect(self.prepare_add_data)
        self.dlg.cancel_btn.clicked.connect(self.dlg.close)
        pass
    def prepare_add_data(self):
        time = self.dlg.time.text()
        event = self.dlg.event.text().replace(" ", "")
        level = self.dlg.level.text().replace(" ", "")
        if len(time.replace(" ", "")) == 0 or len(event) == 0 or len(level) == 0:
            QMessageBox.information(self, '提示', '请输入正确的信息!', QMessageBox.Yes, QMessageBox.Yes)
            return
        self.add_record(time, event, level)
        self.dlg.close()
    def add_record(self, time='', event='', level=1):
        conn = sqlite3.connect("memo.db")
        cursor = conn.cursor()
        # 插入语句
        sql = "insert into record values (NULL, ?, ?, ?)"
        cursor.execute(sql, (time, event, level,))
        conn.commit()
        cursor.close()
        conn.close()
        self.load_record()

数据插入的操作完成了,我们可以随便插入一条数据测试一下。然后会发现在界面上多出了刚刚添加的数据。现在还需要做什么呢?有添加那肯定得有修改和删除对吧?那这个功能放哪里呢?我首先想到的是做一个二级菜单,当鼠标在列表上右键点击的时候,弹出修改和删除的选项,当然还可以做一个双击快速修改的操作。那我们先来完成双击快速修改吧,这个简单一点,信号插槽呢已经在主函数中写好了,也就是主函数中的self.list_view.doubleClicked.connect(self.clickedlist) 这一句。至于什么styleSheet这种css基础就不用讲了吧。

    def clickedlist(self, qModelIndex):
        global data_source
        self.dlg = InputDialog()
        index = qModelIndex.row()
        self.dlg.setStyleSheet(self.Qss)
        self.dlg.load_data(data_source[index][0], data_source[index][1], data_source[index][2], data_source[index][3])
        self.dlg.ok_btn.clicked.connect(self.prepare_update_data)
        self.dlg.cancel_btn.clicked.connect(self.dlg.close)
        pass
    def prepare_update_data(self):
        time = self.dlg.time.text()
        event = self.dlg.event.text().replace(" ", "")
        level = self.dlg.level.text().replace(" ", "")
        if len(time.replace(" ", "")) == 0 or len(event) == 0 or len(level) == 0:
            QMessageBox.information(self, '提示', '请输入正确的信息!', QMessageBox.Yes, QMessageBox.Yes)
            return
        self.update_record(time, event, level, self.dlg.id)
        self.dlg.close()
    def update_record(self, time='', event='', level=1, id=0):
        conn = sqlite3.connect("memo.db")
        cursor = conn.cursor()
        # 更新语句
        sql = "update record set memo_time = ?, memo_event = ?, memo_level = ? where id = ?"
        cursor.execute(sql, (time, event, level, id,))
        conn.commit()
        cursor.close()
        conn.close()
        self.load_record()

可以看到这里也是同样的逻辑,先打开InputDialog(注意,因为这里是修改,所以我们需要调用一下他的load_data方法,load_data的数据来源就是我们之前提及到的全局变量data_source),通过他自动传入的index参数的row()方法来获取点击的对象所在的位置。然后点击确定按钮时调用数据预处理方法prepare_update_data。在update方法中判断数据合法性并调用数据库更新方法。当然,别忘了在更新完成后还要更新UI。

好了,双击更改功能现在已经实现了。接下来要实现的是右键菜单的操作。首先呢,是在主函数中的self.list_view.setContextMenuPolicy(3)以及self.list_view.customContextMenuRequested[QPoint].connect(self.listWidgetContext)这两句,就是为了设置右键菜单,接下来我们看他的实现方法。

    def listWidgetContext(self, point):
        global operate_index
        operate_index = self.list_view.indexAt(point).row()
        popMenu = QMenu()
        actionA = QAction('修改', self)
        actionB = QAction('删除', self)
        popMenu.addAction(actionA)
        popMenu.addAction(actionB)
        actionA.triggered.connect(self.click_menu_to_update)
        actionB.triggered.connect(self.prepare_delete_data)
        popMenu.exec_(QCursor.pos())

注意看,这里的更新操作的方法跟双击的更新方法不一样哦,为什么呢,因为这里传进来的不是列表的某个值了,而是鼠标点击的相对坐标。我们需要根据这个坐标去找到他点的是列表中的第几个。所以用到了operate_index = self.list_view.indexAt(point).row()这个方法。在比较老的版本上可能是itemAt而不是indexAt,这个自己尝试一下就好了。这里又添加了一个全局变量,因为这个参数的传递懒得搞,干脆写个全局的,每次进这个方法就修改成当前的操作对象,也能达到目的。然后是click_menu_to_update方法和prepare_delete_data的内容。

    def click_menu_to_update(self):
        global data_source, operate_index
        self.dlg = InputDialog()
        self.dlg.setStyleSheet(self.Qss)
        self.dlg.load_data(data_source[operate_index][0], data_source[operate_index][1],
                           data_source[operate_index][2], data_source[operate_index][3])
        self.dlg.ok_btn.clicked.connect(self.prepare_update_data)
        self.dlg.cancel_btn.clicked.connect(self.dlg.close)
    def prepare_delete_data(self):
        global operate_index, data_source
        reply = QMessageBox.question(self,
                                     "提示",
                                     "确定要删除吗?",
                                     QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
        if reply == QMessageBox.Yes:
            print('yes')
            self.delete_record(data_source[operate_index][0])
        else:
            pass
    def delete_record(self, id=0):
        conn = sqlite3.connect("memo.db")
        cursor = conn.cursor()
        # 执行一条语句,创建 user表
        sql = "delete from record where id = ?"
        cursor.execute(sql, (id,))
        conn.commit()
        cursor.close()
        conn.close()
        self.load_record()

好了,所有的代码都在这里了,实现逻辑就是点击子菜单的按钮时,先判断是点击的修改还是删除按钮,如果是修改按钮,则打开InputDialog,然后进行数据预处理,再更新数据,最后更新UI;如果是点击的删除按钮,则弹窗提示用户是否确定删除,用户点击确定则调用删除方法,点击取消则关闭弹窗。
————————————————————————————————————
最后附上项目仓库地址:memo
--The End--

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,928评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,192评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,468评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,186评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,295评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,374评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,403评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,186评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,610评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,906评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,075评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,755评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,393评论 3 320
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,079评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,313评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,934评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,963评论 2 351