python小练习--GUI设计

简介: python小练习--GUI设计

练习1:canvas画布的使用


测试画直线,圆形,半圆,三角形等图像,或者插入图片

from tkinter import *
from tkinter import messagebox
#定义坐标位置
lineposition = ((30,50),(300,100),(20,50))
rectposition = ((50,50),(100,100))
ovalposition = ((150,50),(250,150))
arcposition = ((350,50),(250,150))
coorposition = ((100,100),(50,150),(150,150),(80,200),(120,200))
class Application(Frame):
    def __init__(self,master=None):
        super().__init__(master)
        self.master = master
        self.pack()
        self.TestCanvas()
    def TestCanvas(self):
        canvas = Canvas(self, width = 500, height = 350)
        canvas["bg"] = "blue"
        canvas.pack()
  #画直线
        line = canvas.create_line(lineposition)
        #画矩形
        rect = canvas.create_rectangle(rectposition)
        #画圆形
        oval = canvas.create_oval(ovalposition)
        #画四分一圆形
        arc = canvas.create_arc(arcposition)
        #画多边形
        coor1 = canvas.create_polygon(coorposition)
        coor2 = canvas.create_polygon(200,200,400,200,300,300)
  #插入图画
        global photo
        photo = PhotoImage(file="photo/pngphoto/小黄人.png")
        canvas.create_image(380,250,image=photo)
Clichong = Tk()
Clichong.geometry("600x400+100+200")
Clichong.title("GUI设计测试")
app = Application(master=Clichong)
app.mainloop()

image.png

image.png


练习2:练习三个布局管理器


tkinter 提供了三种管理器:pack、grid、place。


1.grid 布局管理器

grid 表格布局,采用表格结构组织组件。子组件的位置由行和列的单元格来确定,并且可以跨行和跨列,从而实现复杂的布局。

image.png

测试1:写一个简单的登录界面

#使用grid布局
from tkinter import *
from tkinter import messagebox
class Application(Frame):
    def __init__(self,master=None):
        super().__init__(master)
        self.master = master
        self.pack()
        self.Mytest()
    def Mytest(self):
        lb1 = Label(self,text="用户名:")
        lb1.grid(row=0,column=0)
        et1 = Entry(self,show="*")
        et1.grid(row=0,column=1)
        bt1 = Button(self,width=5,height=1,text="登录")
        bt1.grid(row=1,column=1,sticky="es")    # sticky控制好方位
Clichong = Tk()
Clichong.geometry("600x400+100+200")
Clichong.title("GUI设计测试")
app = Application(master=Clichong)
app.mainloop()

image.png


测试2:写一个计算器的简单布局

#使用grid布局
from tkinter import *
from tkinter import messagebox
#定义好按钮上面text内容
btnumber = (("MC","M+","M-","MR"),\
            ("C","±","+","*"),\
            (7,8,9,"-"),\
            (4,5,6,"+"),\
            (1,2,3,"="),\
            (0,"."))
class Application(Frame):
    def __init__(self,master=None):
        super().__init__(master)
        self.master = master
        self.pack()
        self.Mytest()
    def Mytest(self):
        Entry(self).grid(row=0,column=0,columnspan=4,pady=10,sticky="nsew")
        print((list(enumerate(btnumber))))
        for rows,datainfo in enumerate(btnumber):
            for nums,singledatainfo in enumerate(datainfo):
                #对于等号与点号需要做特殊在处理
                if singledatainfo == "=":
                    Button(self,text=singledatainfo).grid(row=rows+1,column=nums,rowspan=2,sticky="nsew")
                elif singledatainfo == ".":
                    Button(self, text=singledatainfo).grid(row=rows + 1, column=nums, columnspan=2, sticky="nsew")
                else:
                    Button(self,text=singledatainfo).grid(row=rows+1,column=nums,sticky="nsew")
Clichong = Tk()
Clichong.geometry("140x220+100+200")
Clichong.title("GUI设计测试")
app = Application(master=Clichong)
app.mainloop()

image.png


2.pack 布局管理器

pack 按照组件的创建顺序将子组件添加到父组件中,按照垂直或者水平的方向自然排布。如果不指定任何选项,默认在父组件中自顶向下垂直添加组件。

image.png

测试:制作钢琴按键布局

#使用grid布局
from tkinter import *
from tkinter import messagebox
btstring = ("伴奏1","伴奏2","伴奏3","旋律1","旋律2")
Clichong = Tk()
Clichong.geometry("500x240+300+300")
Clichong.title("钢琴键")
# 思路,这里我们需要定义两个pack,一个放按钮,另外一个放置钢琴键。而这两个pack都在Clichong这个背景上面
# 放置按钮
pianobt = Frame(Clichong)
pianobt.pack(pady=15)
for bttext in btstring:
    Button(pianobt,text=bttext,width=5,height=1).pack(side="left",padx=10)
# 放置钢琴键
pianoky = Frame(Clichong)
pianoky.pack(pady=10)
for btpiano in range(0,11):
    Button(pianoky,width=5,height=10,bg="black" if btpiano%2==0 else "white").pack(side="left")
# 一定要设置循环,不然无法实现
Clichong.mainloop()

image.png


3.place布局管理器

place 布局管理器可以通过坐标精确控制组件的位置,适用于一些布局更加灵活的场景。

image.png


测试1:简单测试place组件

# 测试place布局
from tkinter import *
Clichong = Tk()
Clichong.geometry("400x200+400+300")
Clichong["bg"] = "blue"
# 放置一个黄色的方框
fm1 = Frame(Clichong,bg="yellow",width=100,height=100)
fm1.place(x=30,y=35)
# 放置一个绿色的方框
fm2 = Frame(Clichong,bg="green",width=100,height=100)
fm2.place(x=270,y=35)
# 放置三个按钮
Button(fm1,text="bt1",bg="green").place(relx=0.3,rely=0.7)
Button(fm2,text="bt2",bg="yellow").place(relx=0.7,rely=0.3)
Button(Clichong,text="bt",bg="white").place(width=60,height=30,relx=0.45,rely=0.4)
Clichong.mainloop()

image.png


测试2:扑克牌游戏出牌操作设置

# 扑克出牌
from tkinter import *
Clichong = Tk()
Clichong.geometry("600x400+300+300")
# 图像处理
myphoto = [PhotoImage(file="photo/puke/puke"+str(pukenumber+1)+".gif")for pukenumber in range(10)]
mypuke = [Label(Clichong,image=myphoto[i])for i in range(10)]
# 放置扑克牌
for i in range(10):
    mypuke[i].place(x=50+40*i,y=130)
# 触发事件的操作函数
def pukeoutfunction(event):
    print(event.widget.winfo_geometry())    # 打印geometry信息
    print(event.widget.winfo_y())           # 打印y坐标
    # 如果y坐标在底下,则出牌
    if event.widget.winfo_y() == 130:
        event.widget.place(y=80)
    # 如果y坐标已出,则收牌
    else:
        event.widget.place(y=130)
# 对label事件点击的绑定出牌操作
mypuke[1].bind_class("Label","<Button-1>",pukeoutfunction)
Clichong.mainloop()


初始状态

image.png

点击任意一张牌

image.png

再次点击则收回来

image.png


练习3:鼠标事件和键盘事件用法


 一个 GUI 应用整个生命周期都处在一个消息循环 (event loop) 中。它等待事件的发生,并作出相应的处理。Tkinter 提供了用以处理相关事件的机制. 处理函数可被绑定给各个控件的各种事件。widget.bind(event, handler)如果相关事件发生, handler 函数会被触发, 事件对象event 会传递给 handler 函数.


鼠标和键盘事件:

image.png

event 对象常用属性:

image.png

from tkinter import *
Clichong = Tk()
Clichong.geometry("600x400+300+300")
bt1 = Button(Clichong,bg="blue",text="bt1").place(x=50,y=120,width=50,height=20)
#lb1 = Label(Clichong,bg="pink").place(x=300,y=100,width=100,height=30)
c1 = Canvas(Clichong,width=50,height=50,bg="pink")
c1.place(x=50,y=20)
c2 = Canvas(Clichong,width=200,height=200,bg="orange")
c2.place(x=120,y=20)
# 打印相关信息,测试event 对象常用属性
def mouseTest(event):
    print("鼠标左键单击位置(相对于父容器):{0},{1}".format(event.x,event.y))
    print("鼠标左键单击位置(相对于屏幕):{0},{1}".format(event.x_root,event.y_root))
    print("事件绑定的组件:{0}".format(event.widget))
    print("event.type:{0}".format(event.type))
    print("event.width:{0},event.height:{1}".format(event.width,event.height))
penfont=2         # 设置画笔的粗细
def mouseDrag(event):
    # 不断的画圆,看上去像是画笔,但是event.x+var加大了就知道是个圆
    c2.create_oval(event.x, event.y, event.x+penfont, event.y+penfont)
# 键盘任意按键触发事件,打印按下的键盘键,测试event 对象常用属性
def keyboardTest(event):
    print("按键keycode:{0}\n按键char:{1}\n按键keysym:{2}\n".format(event.keycode,event.char,event.keysym))
# 按键A/a测试
def keyboardTest_A(event):
    print("your press A/a")
def keyboardTest_DoubleA(event):
    print("your press A/a twice times")
def keyboardTest_CtrlA(event):
    print("your press ctrl+A/a")
# 进入c2组件区域测试
def enterTest(event):
    print("event_x:{0},event_y:{1}".format(event.x_root,event.y_root))
# 点击触发事件
c1.bind("<Button-1>",mouseTest)
# 拖动触发事件
c2.bind("<B1-Motion>",mouseDrag)
# 键盘触发事件,一个个测试
# 一般来说,键盘的按键都是绑定主界面
Clichong.bind("<KeyPress>",keyboardTest)
Clichong.bind("<KeyPress-a>",keyboardTest_A)
Clichong.bind("<KeyPress-A>",keyboardTest_A)
Clichong.bind("<Double-KeyPress-a>",keyboardTest_DoubleA)
Clichong.bind("<Control-KeyPress-a>",keyboardTest_CtrlA)
# 鼠标指针进入某一组件区域测试
c2.bind("<Enter>",enterTest)
Clichong.mainloop()

image.png


练习4:lambda 表达式实现传参


lambda 表达式定义的是一个匿名函数,只适合简单输入参数,简单计算返回结果,不适合功能复杂情况。lambda 定义的匿名函数也有输入、也有输出,只是没有名字。

语法格式如下:lambda 参数值列表:表达式


  • 参数值列表即为输入。
  • 表达式计算的结构即为输出。

image.png

from tkinter import *
Clichong = Tk()
Clichong.geometry("270x100")
def MyTest(str1,str2):
    print(str1,"make friend with",str2)
# 使用lambda关键字可以实现传递多个参数操作
Button(Clichong,text="bt1",command=lambda :MyTest("Clichong","Lawrence")).pack()
t1 = lambda x,y:x**y
t2 = lambda :MyTest("A","B")
print("t1(2,3):",t1(2,3),"\n","MyTest(A,B)",t2)
Clichong.mainloop()

image.png


练习5:多种事件绑定方式


  • 组件对象的绑定
  1. 通过 command 属性绑定(适合简单不需获取 event 对象)

Button(root,text=”登录”,command=login)

  1. 通过 bind()方法绑定(适合需要获取 event 对象)


c1 = Canvas(); c1.bind(“”,drawLine)

ps:

command方式绑定,不能直接获取event 对象" "

bind方式绑定,可以获取 event 对象" "


  • 组件类的绑定

调用对象的 bind_class 函数,将该组件类所有的组件绑定事件:w.bind_class(“Widget”,”event”,eventhanler)

比如:btn01.bind_class(“Button”,””,func)


参考前面扑克出牌的小例子:

from tkinter import *
Clichong = Tk()
Clichong.geometry("600x400+300+300")
myphoto = [PhotoImage(file="photo/puke/puke"+str(pukenumber+1)+".gif")for pukenumber in range(10)]
mypuke = [Label(Clichong,image=myphoto[i])for i in range(10)]
for i in range(10):
    mypuke[i].place(x=50+40*i,y=130)
def pukeoutfunction(event):
    print(event.widget.winfo_geometry())    
    print(event.widget.winfo_y())         
    if event.widget.winfo_y() == 130:
        event.widget.place(y=80)
    else:
        event.widget.place(y=130)
# 对label事件点击的绑定出牌操作,bind_class直接绑定label这整一类
mypuke[1].bind_class("Label","<Button-1>",pukeoutfunction)
Clichong.mainloop()


练习6:OptionMenu 选择项


介绍:

class OptionMenu(Menubutton):
    """OptionMenu which allows the user to select a value from a menu."""
    def __init__(self, master, variable, value, *values, **kwargs):
        """Construct an optionmenu widget with the parent MASTER, with
        the resource textvariable set to VARIABLE, the initially selected
        value VALUE, the other menu values VALUES and an additional
        keyword argument command."""
        kw = {"borderwidth": 2, "textvariable": variable,
              "indicatoron": 1, "relief": RAISED, "anchor": "c",
              "highlightthickness": 2}


测试:

from tkinter import *
Clichong = Tk()
Clichong.geometry("400x300+300+300")
# 设置变量,方便回去选择项获取的内容,与其他空间类型操作
opmfirstvar = StringVar()
opmfirstvar.set("蔬菜面")
opm1 = OptionMenu(Clichong,opmfirstvar,"蔬菜面","兰州拉面","豚骨拉面","地狱拉面")
opm1.place(x=50,y=30,width=120,height=40)
def ShowLabel():
    print(opmfirstvar.get())
bt1 = Button(Clichong,width=3,command=ShowLabel).place(x=50,y=100)
Clichong.mainloop()

image.png


练习7:Scale移动滑块


介绍:

class Scale(Widget):
    """Scale widget which can display a numerical scale."""
    def __init__(self, master=None, cnf={}, **kw):
        """Construct a scale widget with the parent MASTER.
        Valid resource names: activebackground, background, bigincrement, bd,
        bg, borderwidth, command, cursor, digits, fg, font, foreground, from,
        highlightbackground, highlightcolor, highlightthickness, label,
        length, orient, relief, repeatdelay, repeatinterval, resolution,
        showvalue, sliderlength, sliderrelief, state, takefocus,
        tickinterval, to, troughcolor, variable, width."""
        Widget.__init__(self, master, 'scale', cnf, kw)


测试:

from tkinter import *
Clichong = Tk()
Clichong.geometry("400x300")
lb1 = Label(Clichong,text="Clichong",width=10,height=2,font=("宋体",20))
lb1.pack()
# label不支持以下的操作:TypeError: 'NoneType' object does not support item assignment
# lb1["font"] = ("宋体",20)
# 通过函数传递当前值
def mytest(value):
    print(value)
    newFont = ("黑体", value)
    lb1.config(font=newFont)
sc1 = Scale(Clichong,from_=10,to=100,length=300,width=30,command=mytest)
# 组件Scale可以支持如下操作
sc1["orient"] = HORIZONTAL
sc1.pack()
Clichong.mainloop()


未调整前:

image.png

调整之后:

image.png


练习8:字体动态调整的方法测试


基于以上字体调整失败,此部分再次进行调试

from tkinter import *
Clichong = Tk()
Clichong.geometry("400x300")
lb1 = Label(Clichong,text="Clichong",width=10,height=2,font=("宋体",20))
lb1.pack()
# 经测试,以下两种动态调整字体的方法都可以
def func1():
    lb1["font"] = ("黑体", 40)
def func2():
    lb1.config(font=("黑体",30))
bt1 = Button(Clichong,command=func1).pack(side="left")
bt2 = Button(Clichong,command=func2).pack(side="left")
Clichong.mainloop()


按钮1:

image.png

按钮2:

image.png


练习9:颜色选择框基本用法


颜色选择框可以帮助我们设置背景色、前景色、画笔颜色、字体颜色等等。

# 颜色选择框
from tkinter import *
from tkinter.colorchooser import *
Clichong = Tk()
Clichong.geometry("400x300")
def mytest():
    # 设置初始选择的颜色是绿色,颜色框标题是"My color"
    cr = askcolor(color="green",title="My color")
    # 得出的结果是((128.5, 255.99609375, 255.99609375), '#80ffff')
    print(cr)
    # 设置背景颜色,以下两种方法均可
    Clichong["bg"] = cr[1]
    Clichong.config(bg = cr[1])
Button(Clichong,text="选择背景色",command=mytest).pack()
Clichong.mainloop()


调出颜色选择框:

image.png

image.png

点击确定更换背景色

image.png


练习10:文件对话框基本用法


文件对话框帮助我们实现可视化的操作目录、操作文件。最后,将文件、目录的信息传入到程序中。文件对话框包含如下一些常用函数:

image.png

image.png

命名参数 options 的常见值如下:

image.png


测试1:返回选择好的文件名

# 文件选择框
from tkinter import *
from  tkinter.filedialog import *
Clichong = Tk()
Clichong.geometry("400x300")
def mytest():
    askfile = askopenfilename(title="My File")
    lb1["text"] = askfile
Button(Clichong,command=mytest).pack()
lb1 = Label(Clichong,width=40,height=3,bg="green")
lb1.pack()
Clichong.mainloop()


点击按钮弹出文件选择框:

image.png

选择好后在label显示文件名

image.png


测试2:打开指定 txt 文件,并读出文件内容到窗口

# 文件选择框
from tkinter import *
from  tkinter.filedialog import *
Clichong = Tk()
Clichong.geometry("400x300")
def mytest1():
    askfile = askopenfilename(title="My File")
    lb1["text"] = askfile
def mytest2():
    with askopenfile(title="File") as fd:
        lb2.config(text=fd.read())
Button(Clichong,command=mytest1).pack(side="left")
Button(Clichong,command=mytest2).pack(side="left")
lb1 = Label(Clichong,width=40,height=3,bg="pink")
lb1.pack()
lb2 = Label(Clichong,width=40,height=10,bg="blue")
lb2.pack()
Clichong.mainloop()


测试按钮1:

image.png

输出文件路劲

image.png

测试按钮2:

image.png

输出文件内容

image.png


练习11:简单输入对话框


simpledialog(简单对话框)包含如下常用函数:

image.png

参数中,title 表示窗口标题;prompt 是提示信息;命名参数kw 为各种选项:initialvalue(初始值)、minvalue(最小值)、maxvalue(最大值)。

# 颜色选择框
from tkinter import *
from tkinter.simpledialog import *
Clichong = Tk()
Clichong.geometry("400x300")
lb1 = Label(Clichong,width=40,height=3,bg= "green")
lb1.pack()
# Button(Clichong,text="选择背景色",command=mytest).pack()
getstr = askinteger(title="about age",prompt="input your age",initialvalue=0,minvalue=0,maxvalue=100)
lb1.config(text=getstr)
Clichong.mainloop()

image.png

超过100会警告:

image.png

image.png


练习12:通用消息框


messagebox(通用消息框)用于和用户简单的交互,用户点击确定、取消。如下列出了 messagebox 的常见函数:

image.png

测试代码:

from tkinter import *
from tkinter.messagebox import *
Clichong = Tk()
Clichong.geometry("400x300")
# box1 = askokcancel(Clichong,"你是男的女的")
# box2 = askquestion(title="问题1",message="你是男的女的")
# box3 = askyesnocancel(title="问题1",message="你是男的女的")
# box4 = showinfo(title="问题1",message="你是男的女的")
box5 = showerror(title="问题1",message="你是男的女的")
Clichong.mainloop()

image.png


  • ttk 子模块控件

我们再前面学的组件是 tkinter 模块下的组件,整体风格较老较丑。为了弥补这点不足,推出了 ttk 组件。ttk 组件更加美观、功能更加强大。使用 Combobox 替代了原来的Listbox 、 新 增 了 LabeledScale( 带 标 签 的 Scale) 、Notebook(多文档窗口)、Progressbar(进度条)、Treeview(数)等组件。

使用 ttk 组件与使用普通的 Tkinter 组件并没有多大的

区别,只要导入 ttk 模块即可。


目录
相关文章
|
3月前
|
测试技术 开发者 Python
Python(GUI)之活动积分记录表
本文介绍了一套使用Python的Tkinter库构建的学生活动积分记录系统。该系统允许教师选择班级和学生,输入加分分数及原因,并将数据保存至文件,旨在简化学生积分管理流程,提升教学效率。
74 6
|
3月前
|
设计模式 开发者 Python
Python编程中的设计模式:工厂方法模式###
本文深入浅出地探讨了Python编程中的一种重要设计模式——工厂方法模式。通过具体案例和代码示例,我们将了解工厂方法模式的定义、应用场景、实现步骤以及其优势与潜在缺点。无论你是Python新手还是有经验的开发者,都能从本文中获得关于如何在实际项目中有效应用工厂方法模式的启发。 ###
|
3月前
|
设计模式 开发者 Python
Python编程中的设计模式应用与实践感悟####
本文作为一篇技术性文章,旨在深入探讨Python编程中设计模式的应用价值与实践心得。在快速迭代的软件开发领域,设计模式如同导航灯塔,指引开发者构建高效、可维护的软件架构。本文将通过具体案例,展现设计模式如何在实际项目中解决复杂问题,提升代码质量,并分享个人在实践过程中的体会与感悟。 ####
|
3月前
|
设计模式 算法 搜索推荐
Python编程中的设计模式:优雅解决复杂问题的钥匙####
本文将探讨Python编程中几种核心设计模式的应用实例与优势,不涉及具体代码示例,而是聚焦于每种模式背后的设计理念、适用场景及其如何促进代码的可维护性和扩展性。通过理解这些设计模式,开发者可以更加高效地构建软件系统,实现代码复用,提升项目质量。 ####
|
3月前
|
设计模式 监控 算法
Python编程中的设计模式应用与实践感悟###
在Python这片广阔的编程疆域中,设计模式如同导航的灯塔,指引着开发者穿越复杂性的迷雾,构建出既高效又易于维护的代码结构。本文基于个人实践经验,深入探讨了几种核心设计模式在Python项目中的应用策略与实现细节,旨在为读者揭示这些模式背后的思想如何转化为提升软件质量的实际力量。通过具体案例分析,展现了设计模式在解决实际问题中的独特魅力,鼓励开发者在日常编码中积极采纳并灵活运用这些宝贵的经验总结。 ###
|
3月前
|
数据可视化 开发者 Python
Python GUI开发:Tkinter与PyQt的实战应用与对比分析
【10月更文挑战第26天】本文介绍了Python中两种常用的GUI工具包——Tkinter和PyQt。Tkinter内置于Python标准库,适合初学者快速上手,提供基本的GUI组件和方法。PyQt基于Qt库,功能强大且灵活,适用于创建复杂的GUI应用程序。通过实战示例和对比分析,帮助开发者选择合适的工具包以满足项目需求。
274 7
|
3月前
|
设计模式 监控 数据库连接
Python编程中的设计模式之美:提升代码质量与可维护性####
【10月更文挑战第21天】 一段简短而富有启发性的开头,引出文章的核心价值所在。 在编程的世界里,设计模式如同建筑师手中的蓝图,为软件的设计和实现提供了一套经过验证的解决方案。本文将深入浅出地探讨Python编程中几种常见的设计模式,通过实例展示它们如何帮助我们构建更加灵活、可扩展且易于维护的代码。 ####
|
4月前
|
设计模式 开发者 Python
Python编程中的设计模式:从入门到精通####
【10月更文挑战第14天】 本文旨在为Python开发者提供一个关于设计模式的全面指南,通过深入浅出的方式解析常见的设计模式,帮助读者在实际项目中灵活运用这些模式以提升代码质量和可维护性。文章首先概述了设计模式的基本概念和重要性,接着逐一介绍了几种常用的设计模式,并通过具体的Python代码示例展示了它们的实际应用。无论您是Python初学者还是经验丰富的开发者,都能从本文中获得有价值的见解和实用的技巧。 ####
|
4月前
|
设计模式 开发者 Python
Python编程中的设计模式应用与实践###
【10月更文挑战第18天】 本文深入探讨了Python编程中设计模式的应用与实践,通过简洁明了的语言和生动的实例,揭示了设计模式在提升代码可维护性、可扩展性和重用性方面的关键作用。文章首先概述了设计模式的基本概念和重要性,随后详细解析了几种常用的设计模式,如单例模式、工厂模式、观察者模式等,在Python中的具体实现方式,并通过对比分析,展示了设计模式如何优化代码结构,增强系统的灵活性和健壮性。此外,文章还提供了实用的建议和最佳实践,帮助读者在实际项目中有效运用设计模式。 ###
41 0
|
4月前
|
设计模式 存储 数据库连接
Python编程中的设计模式之美:单例模式的妙用与实现###
本文将深入浅出地探讨Python编程中的一种重要设计模式——单例模式。通过生动的比喻、清晰的逻辑和实用的代码示例,让读者轻松理解单例模式的核心概念、应用场景及如何在Python中高效实现。无论是初学者还是有经验的开发者,都能从中获得启发,提升对设计模式的理解和应用能力。 ###

热门文章

最新文章