【qt】一个WPS项目了解qt界面设计的基本套路
国中之林 2024-10-08 08:05:06 阅读 66
项目功能演示:
放心食用!最后有完整代码.
超级详细,期待您的一个点赞❥(^_-)
一览全局:
WPS项目目录
一.创建项目二.导入资源三.ui设计四.字号选择框初始化五.滚动条初始化六.添加自定义文本类七.初始化action状态八.新建文档九.打开文件十.保存与另存为十一.打印/打印预览十二.撤销重做剪切复制粘贴十三.关闭事件/退出十四.窗体菜单实现十五.动态添加action/信号映射器十六.字体颜色加粗斜体下划线十七.文本对齐十八.段落符号十九.字体字号二十.打包二十一.发布二十二.完整代码二十三.最后
一.创建项目
用的是QMainWindow类,这个包含菜单,工具栏,状态栏.
能运行出来,就说明环境搭好了.
二.导入资源
三.ui设计
应用图标的添加.
四.字号选择框初始化
获取系统可支持的字号添加到选择框中,再设置当前系统字号
五.滚动条初始化
滚动条就是这个,这是用的后面做完的来演示的.
六.添加自定义文本类
记得添加宏<code>Q_OBJECT,还有初始化父类
七.初始化action状态
八.新建文档
九.打开文件
十.保存与另存为
十一.打印/打印预览
十二.撤销重做剪切复制粘贴
十三.关闭事件/退出
十四.窗体菜单实现
十五.动态添加action/信号映射器
十六.字体颜色加粗斜体下划线
十七.文本对齐
十八.段落符号
<code>void MainWindow::on_comboBoxStandard_activated(int index)
{ -- -->
TextEdit*edit=activateWindow();
if(edit)
{
if(index==0)
{
QTextCursor cursor=edit->textCursor();
cursor.beginEditBlock();
QTextList*list=cursor.currentList();
if(list)
{
list->remove(cursor.block());
QTextBlockFormat blockFormat=cursor.blockFormat();
blockFormat.setIndent(0);
cursor.setBlockFormat(blockFormat);
}
cursor.endEditBlock();
return;
}
QTextListFormat::Style style;//描述装饰列表项符号的枚举
switch (index)
{
case 1:
style=QTextListFormat::ListDisc;
break;
case 2:
style=QTextListFormat::ListCircle;
break;
case 3:
style=QTextListFormat::ListSquare;
break;
case 4:
style=QTextListFormat::ListDecimal;
break;
case 5:
style=QTextListFormat::ListLowerAlpha;
break;
case 6:
style=QTextListFormat::ListUpperAlpha;
break;
case 7:
style=QTextListFormat::ListLowerRoman;
break;
case 8:
style=QTextListFormat::ListUpperRoman;
break;
default:
style=QTextListFormat::ListStyleUndefined;//0
break;
}
QTextCursor cursor=edit->textCursor();
cursor.beginEditBlock();
QTextBlockFormat blockFormat=cursor.blockFormat();
QTextListFormat listFormat;
QTextList*list=cursor.currentList();
if(list)
{
listFormat=list->format();
list->remove(cursor.block());
blockFormat.setIndent(0);
cursor.setBlockFormat(blockFormat);
}
listFormat.setStyle(style);
cursor.createList(listFormat);
cursor.endEditBlock();
}
}
十九.字体字号
二十.打包
二十一.发布
二十二.完整代码
main.cpp
<code>#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{ -- -->
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "textedit.h"
#include <QMdiSubWindow>
#include <QPrinter>
#include <QActionGroup>
#include <QSignalMapper>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void on_NewAction_triggered();
void setActionStatus(bool b);
void on_OpenAction_triggered();
void on_saveAction_triggered();
void on_saveAsAction_triggered();
void on_printAction_triggered();
void on_printViewAction_triggered();
void printPreview(QPrinter*printer);
void on_undoAction_triggered();
void on_redoAction_triggered();
void on_cutAction_triggered();
void on_copyAction_triggered();
void on_pasteAction_triggered();
void on_exitAction_triggered();
void on_closeAction_triggered();
void on_closeAllAction_triggered();
void on_tileAction_triggered();
void on_cascadeAction_triggered();
void on_nextAction_triggered();
void on_previousAction_triggered();
void addSubWindowAction();
void setActiveSubWindow(QWidget*widget);
void on_colorAction_triggered();
void on_blodAction_triggered();
void on_italicAction_triggered();
void on_underLineAction_triggered();
void on_leftAlignAction_triggered();
void on_centerAction_triggered();
void on_rightAlignAction_triggered();
void on_justifyAction_triggered();
void on_comboBoxStandard_activated(int index);
void on_fontComboBox_activated(const QString &arg1);
void on_comboBoxFontSize_activated(const QString &arg1);
protected:
void closeEvent(QCloseEvent *event)override;//重写关闭事件
private:
Ui::MainWindow *ui;
void init();//初始化函数
void initFontSize();//初始化字号
void initScrollBar();//初始化滚动条
void initWindowAction();//初始化窗体相关的action
void initDocAction();//初始化文本相关的action
TextEdit* activateWindow();//获取当前活动窗口
QMdiSubWindow * findSubWindow(const QString&docName);//获取相应文档的子窗体
QActionGroup*actionGroup;
QSignalMapper*mapper;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QFontDatabase>
#include <QMdiSubWindow>
#include <QFileDialog>
#include <QFileInfo>
#include <QPrinter>
#include <QPrintDialog>
#include <QPrintPreviewDialog>
#include <QCloseEvent>
#include <QColorDialog>
#include <QTextCharFormat>
#include <QTextList>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
init();
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::init()
{
ui->mdiArea->setBackground(QBrush(Qt::white));//用画刷设置mdi背景颜色
initFontSize();
initScrollBar();
initWindowAction();
//当窗口关闭时发送一次信号,重新设置窗口action. initWindowAction
connect(ui->mdiArea,&QMdiArea::subWindowActivated,this,&MainWindow::initWindowAction);
connect(ui->mdiArea,&QMdiArea::subWindowActivated,this,&MainWindow::initDocAction);
initDocAction();
actionGroup=new QActionGroup(this);
actionGroup->setExclusive(true);//排他选择
mapper=new QSignalMapper(this);
connect(ui->menu_W,&QMenu::aboutToShow,this,&MainWindow::addSubWindowAction);
connect(mapper,SIGNAL(mapped(QWidget*)),this,SLOT(setActiveSubWindow(QWidget*)));
}
void MainWindow::initFontSize()
{
ui->comboBoxFontSize->clear();
//QFontDatabase提供系统可用字体的相关信息
for(int fontSize:QFontDatabase::standardSizes())//返回标准字体大小的列表
{
ui->comboBoxFontSize->addItem(QString::number(fontSize));//插入到字体选择框中
}
QFont appFont=QApplication::font();//当前系统默认字体
int fontSize=appFont.pointSize(); //获取字体号
int sizeIndex=ui->comboBoxFontSize->findText(QString::number(fontSize));
ui->comboBoxFontSize->setCurrentIndex(sizeIndex);
}
void MainWindow::initScrollBar()
{
//设置mdi的水平和垂直滚动条当需要的时候
ui->mdiArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
ui->mdiArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
}
void MainWindow::initWindowAction()
{
bool hasActWindow=(activateWindow()!=nullptr);
ui->saveAction->setEnabled(hasActWindow);
ui->saveAsAction->setEnabled(hasActWindow);
ui->printAction->setEnabled(hasActWindow);
ui->printViewAction->setEnabled(hasActWindow);
ui->undoAction->setEnabled(hasActWindow);
ui->redoAction->setEnabled(hasActWindow);
ui->pasteAction->setEnabled(hasActWindow);
ui->closeAction->setEnabled(hasActWindow);
ui->closeAllAction->setEnabled(hasActWindow);
ui->tileAction->setEnabled(hasActWindow);
ui->cascadeAction->setEnabled(hasActWindow);
ui->nextAction->setEnabled(hasActWindow);
ui->previousAction->setEnabled(hasActWindow);
}
void MainWindow::initDocAction()
{
ui->cutAction->setEnabled(false);
ui->copyAction->setEnabled(false);
ui->colorAction->setEnabled(false);
ui->blodAction->setEnabled(false);
ui->italicAction->setEnabled(false);
ui->underLineAction->setEnabled(false);
ui->leftAlignAction->setEnabled(false);
ui->centerAction->setEnabled(false);
ui->rightAlignAction->setEnabled(false);
ui->justifyAction->setEnabled(false);
}
TextEdit *MainWindow::activateWindow()
{
QMdiSubWindow*window=ui->mdiArea->activeSubWindow();
if(window)
{
return qobject_cast<TextEdit*>(window->widget());
}
return nullptr;
}
QMdiSubWindow *MainWindow::findSubWindow(const QString &docName)
{
QString filepath=QFileInfo(docName).canonicalFilePath();//返回文件的绝对路径
for(QMdiSubWindow*sub:ui->mdiArea->subWindowList())
{
TextEdit* edit=qobject_cast<TextEdit*>(sub->widget());
if(edit->getDocFilePath()==filepath)
{
return sub;
}
}
return nullptr;
}
void MainWindow::on_NewAction_triggered()
{
TextEdit*edit=new TextEdit;
ui->mdiArea->addSubWindow(edit);//edit父对象将是QMdiArea的viewport小部件
//当文本编辑器选中或者取消选中会发出copyAvailable信号.
connect(edit,&TextEdit::copyAvailable,this,&MainWindow::setActionStatus);
edit->initNewDoc();
edit->show();
}
void MainWindow::setActionStatus(bool b)
{
ui->cutAction->setEnabled(b);
ui->copyAction->setEnabled(b);
ui->colorAction->setEnabled(b);
ui->blodAction->setEnabled(b);
ui->italicAction->setEnabled(b);
ui->underLineAction->setEnabled(b);
ui->leftAlignAction->setEnabled(b);
ui->centerAction->setEnabled(b);
ui->rightAlignAction->setEnabled(b);
ui->justifyAction->setEnabled(b);
}
void MainWindow::on_OpenAction_triggered()
{
QString docName=QFileDialog::getOpenFileName(this,"打开文档","./","所以文件(*.*)");
if(!docName.isEmpty())
{
QMdiSubWindow*sub=findSubWindow(docName);
if(sub)
{
ui->mdiArea->setActiveSubWindow(sub);
return;
}
TextEdit*edit=new TextEdit;
ui->mdiArea->addSubWindow(edit);
connect(edit,&TextEdit::copyAvailable,this,&MainWindow::setActionStatus);
if(edit->loadDoc(docName))
{
statusBar()->showMessage("文档已打开",3000);//3秒显示在状态栏
edit->show();
}
else
{
edit->close();
}
}
}
void MainWindow::on_saveAction_triggered()
{
TextEdit* edit=activateWindow();
if(edit)
{
if(edit->saveDoc())
{
statusBar()->showMessage("文档已保存",3000);
}
}
}
void MainWindow::on_saveAsAction_triggered()
{
TextEdit* edit=activateWindow();
if(edit)
{
if(edit->saveAsDoc())
{
statusBar()->showMessage("文档已保存",3000);
}
}
}
void MainWindow::on_printAction_triggered()
{
TextEdit* edit=activateWindow();
if(!edit) return;
//QPrinter::HighResolution将printer分辨率设置为正在使用的打印机分辨率
QPrinter printer(QPrinter::HighResolution);
QPrintDialog dialog(&printer,this);
if(dialog.exec()==QDialog::Accepted)
{
edit->print(&printer);
}
}
void MainWindow::on_printViewAction_triggered()
{
TextEdit* edit=activateWindow();
if(!edit) return;
QPrinter printer;
QPrintPreviewDialog preview(&printer,this);
connect(&preview,&QPrintPreviewDialog::paintRequested,this,&MainWindow::printPreview);
if(preview.exec()==QPrintPreviewDialog::Accepted)
{
edit->print(&printer);
}
}
void MainWindow::printPreview(QPrinter*printer)
{
TextEdit* edit=activateWindow();
if(edit)
{
edit->print(printer);
}
}
void MainWindow::on_undoAction_triggered()
{
TextEdit*edit=activateWindow();
if(edit)
{
edit->undo();
}
}
void MainWindow::on_redoAction_triggered()
{
TextEdit*edit=activateWindow();
if(edit)
{
edit->redo();
}
}
void MainWindow::on_cutAction_triggered()
{
TextEdit*edit=activateWindow();
if(edit)
{
edit->cut();
}
}
void MainWindow::on_copyAction_triggered()
{
TextEdit*edit=activateWindow();
if(edit)
{
edit->copy();
}
}
void MainWindow::on_pasteAction_triggered()
{
TextEdit*edit=activateWindow();
if(edit)
{
edit->paste();
}
}
void MainWindow::closeEvent(QCloseEvent *event)
{
ui->mdiArea->closeAllSubWindows();
if(ui->mdiArea->currentSubWindow())
{
event->ignore();
}
else
{
event->accept();
}
}
void MainWindow::on_exitAction_triggered()
{
close();
}
void MainWindow::on_closeAction_triggered()
{
ui->mdiArea->closeActiveSubWindow();
}
void MainWindow::on_closeAllAction_triggered()
{
ui->mdiArea->closeAllSubWindows();
}
void MainWindow::on_tileAction_triggered()
{
ui->mdiArea->tileSubWindows();
}
void MainWindow::on_cascadeAction_triggered()
{
ui->mdiArea->cascadeSubWindows();
}
void MainWindow::on_nextAction_triggered()
{
ui->mdiArea->activateNextSubWindow();
}
void MainWindow::on_previousAction_triggered()
{
ui->mdiArea->activatePreviousSubWindow();
}
void MainWindow::addSubWindowAction()
{
QList<QAction*> actionList=actionGroup->actions();
if(!actionList.isEmpty())
{
for(QAction*action:actionList)
{
delete action;
}
}
QList<QMdiSubWindow*>subWindowList=ui->mdiArea->subWindowList();//获取子窗体列表
if(!subWindowList.isEmpty())ui->menu_W->addSeparator();//添加一个分隔符
for(int i=0;i<subWindowList.count();i++)
{
QMdiSubWindow*subWindow=subWindowList[i];
TextEdit*edit=qobject_cast<TextEdit*>(subWindow->widget());
QString action_text=QString("%1 %2").arg(i+1).arg(edit->getDocWindowTitle());
QAction* action=ui->menu_W->addAction(action_text);
actionGroup->addAction(action);
action->setCheckable(true);
if(edit==activateWindow())action->setChecked(true);//设置选中
connect(action,SIGNAL(triggered(bool)),mapper,SLOT(map()));
mapper->setMapping(action,subWindow);
}
}
void MainWindow::setActiveSubWindow(QWidget *widget)
{
if(widget)ui->mdiArea->setActiveSubWindow(qobject_cast<QMdiSubWindow*>(widget));
}
void MainWindow::on_colorAction_triggered()
{
TextEdit*edit=activateWindow();
if(edit)
{
QColor color;
color=QColorDialog::getColor();
if(!color.isValid())return ;
QTextCharFormat format;
format.setForeground(color);
edit->mergeCurrentCharFormat(format);
QPixmap pixmap(24,24);
pixmap.fill(color);
ui->colorAction->setIcon(pixmap);
}
}
void MainWindow::on_blodAction_triggered()
{
TextEdit*edit=activateWindow();
if(edit)
{
bool ischecked=ui->blodAction->isChecked();
QTextCharFormat format;
format.setFontWeight(ischecked?QFont::Bold:QFont::Normal);
edit->mergeCurrentCharFormat(format);
}
}
void MainWindow::on_italicAction_triggered()
{
TextEdit*edit=activateWindow();
if(edit)
{
QTextCharFormat format;
format.setFontItalic(ui->italicAction->isChecked());
edit->mergeCurrentCharFormat(format);
}
}
void MainWindow::on_underLineAction_triggered()
{
TextEdit*edit=activateWindow();
if(edit)
{
QTextCharFormat format;
format.setFontUnderline(ui->underLineAction->isChecked());
edit->mergeCurrentCharFormat(format);
}
}
void MainWindow::on_leftAlignAction_triggered()
{
TextEdit*edit=activateWindow();
if(edit)
{
edit->setAlignment(Qt::AlignLeft);
}
}
void MainWindow::on_centerAction_triggered()
{
TextEdit*edit=activateWindow();
if(edit)
{
edit->setAlignment(Qt::AlignCenter);
}
}
void MainWindow::on_rightAlignAction_triggered()
{
TextEdit*edit=activateWindow();
if(edit)
{
edit->setAlignment(Qt::AlignRight);
}
}
void MainWindow::on_justifyAction_triggered()
{
TextEdit*edit=activateWindow();
if(edit)
{
edit->setAlignment(Qt::AlignJustify);
}
}
void MainWindow::on_comboBoxStandard_activated(int index)
{
TextEdit*edit=activateWindow();
if(edit)
{
if(index==0)
{
QTextCursor cursor=edit->textCursor();
cursor.beginEditBlock();
QTextList*list=cursor.currentList();
if(list)
{
list->remove(cursor.block());
QTextBlockFormat blockFormat=cursor.blockFormat();
blockFormat.setIndent(0);
cursor.setBlockFormat(blockFormat);
}
cursor.endEditBlock();
return;
}
QTextListFormat::Style style;//描述装饰列表项符号的枚举
switch (index)
{
case 1:
style=QTextListFormat::ListDisc;
break;
case 2:
style=QTextListFormat::ListCircle;
break;
case 3:
style=QTextListFormat::ListSquare;
break;
case 4:
style=QTextListFormat::ListDecimal;
break;
case 5:
style=QTextListFormat::ListLowerAlpha;
break;
case 6:
style=QTextListFormat::ListUpperAlpha;
break;
case 7:
style=QTextListFormat::ListLowerRoman;
break;
case 8:
style=QTextListFormat::ListUpperRoman;
break;
default:
style=QTextListFormat::ListStyleUndefined;//0
break;
}
QTextCursor cursor=edit->textCursor();
cursor.beginEditBlock();
QTextBlockFormat blockFormat=cursor.blockFormat();
QTextListFormat listFormat;
QTextList*list=cursor.currentList();
if(list)
{
listFormat=list->format();
list->remove(cursor.block());
blockFormat.setIndent(0);
cursor.setBlockFormat(blockFormat);
}
listFormat.setStyle(style);
cursor.createList(listFormat);
cursor.endEditBlock();
}
}
void MainWindow::on_fontComboBox_activated(const QString &arg1)
{
TextEdit* edit=activateWindow();
if(edit)
{
QTextCharFormat format;
format.setFontFamily(arg1);
edit->mergeCurrentCharFormat(format);
}
}
void MainWindow::on_comboBoxFontSize_activated(const QString &arg1)
{
TextEdit* edit=activateWindow();
if(edit)
{
QTextCharFormat format;
format.setFontPointSize(arg1.toInt());
edit->mergeCurrentCharFormat(format);
}
}
textedit.h
#ifndef TEXTEDIT_H
#define TEXTEDIT_H
#include <QTextEdit>
class TextEdit : public QTextEdit
{
Q_OBJECT
public:
TextEdit(QWidget*parent=nullptr);
~TextEdit();
void initNewDoc(); //初始化新建文档
QString getDocFilePath()const;//获取文件路径
bool loadDoc(const QString&docName);//读取文件内容
bool saveDoc();//保存文档
bool saveAsDoc();//另存为
QString getDocWindowTitle()const;
protected:
void closeEvent(QCloseEvent *event)override;//重写关闭事件
private:
QString getDocName()const;
void initOpenDoc(const QString&docName);//初始化打开的文档
bool writeToDoc(const QString&docName);//写文件
bool promptSave();//提示是否保存
private slots:
void setWindowModality();//设置WindowModality属性
private:
QString docWindowTitle; //文档标题
static int docNo;//文档编号
QString docFilePath;//文档路径
};
#endif // TEXTEDIT_H
textedit.cpp
#include "textedit.h"
#include <QFile>
#include <QFileInfo>
#include <QFileDialog>
#include <QTextDocumentWriter>
#include <QCloseEvent>
#include <QMessageBox>
int TextEdit::docNo=1;
TextEdit::TextEdit(QWidget *parent):QTextEdit(parent)
{
setAttribute(Qt::WA_DeleteOnClose);//关闭窗口时,释放资源
docWindowTitle="";code>
docFilePath="";//路径初始化为空可以区分是否是打开的文档code>
}
TextEdit::~TextEdit()
{ -- -->
}
void TextEdit::initNewDoc()
{
docWindowTitle=QString("文档 %1").arg(docNo++);
//使用windowModified机制,添加[*]占位符
//当窗口显示的文档未保存时,*就显示出来
setWindowTitle(docWindowTitle + "[*]");
//当文档内容发生改变,发出contentsChanged信号
//document() 返回当前TextEdit文档
connect(document(),&QTextDocument::contentsChanged,this,&TextEdit::setWindowModality);
}
QString TextEdit::getDocFilePath() const
{
return this->docFilePath;
}
QString TextEdit::getDocName() const
{
return QFileInfo(this->docFilePath).fileName();//获取文件名称
}
bool TextEdit::loadDoc(const QString &docName)
{
if(!docName.isEmpty())
{
QFile file(docName);
if(!file.exists())return false;//文件不存在
if(!file.open(QFile::ReadOnly))return false;//打开文件失败
QByteArray text=file.readAll();//读取文件内容
if(Qt::mightBeRichText(text))//如果是富文件
{
setHtml(text);//富文本显示
}
else
{
setPlainText(text);//纯文本显示
}
initOpenDoc(docName);
}
return true;
}
bool TextEdit::saveDoc()
{
if(document()->isModified())//文档是否被修改
{
if(!docFilePath.isEmpty())//路径是否为空
{
return writeToDoc(docFilePath);//写文件
}
else
{
return saveAsDoc();//另存为
}
}
return false;
}
bool TextEdit::saveAsDoc()
{
QString docName=QFileDialog::getSaveFileName(this,"另存为","./","HTML文档(*.html);;文本文件(*.txt)");
if(docName.isEmpty())return false;
return writeToDoc(docName);
}
QString TextEdit::getDocWindowTitle() const
{
return this->docWindowTitle;
}
bool TextEdit::promptSave()
{
if(!document()->isModified())
{
return true;
}
QMessageBox::StandardButton res;
res=QMessageBox::warning(this,"提示",QString("%1已修改,是否保存?").arg(docWindowTitle),
QMessageBox::Yes|QMessageBox::No|QMessageBox::Cancel);
if(res==QMessageBox::Yes)
{
return saveDoc();
}
else if(res==QMessageBox::No)
{
return true;
}
else if(res==QMessageBox::Cancel)
{
return false;
}
}
void TextEdit::closeEvent(QCloseEvent *event)
{
if(promptSave())
{
event->accept();
}
else
{
event->ignore();
}
}
void TextEdit::initOpenDoc(const QString &docName)
{
docFilePath=QFileInfo(docName).canonicalFilePath();
docWindowTitle=getDocName()+" "+QString::number(docNo++);
setWindowTitle(docWindowTitle + "[*]");
connect(document(),&QTextDocument::contentsChanged,this,&TextEdit::setWindowModality);
}
bool TextEdit::writeToDoc(const QString &docName)
{
//QTextDocumentWriter将QTextDocument写入文件
QTextDocumentWriter docWriter(docName);
if(docWriter.write(document()))
{
docFilePath=QFileInfo(docName).canonicalFilePath();
document()->setModified(false);//文档未改动
setWindowModified(false);//不显示占位符标识
}
return true;
}
void TextEdit::setWindowModality()
{
setWindowModified(document()->isModified());//根据是否改动来设置是否显示*
}
二十三.最后
现在是2024年9月20日15:57:22,让时间见证这一切吧,继续前进!
声明
本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。