效果

python 滚动窗口 多列计算 python滚动文本框_ide

python 滚动窗口 多列计算 python滚动文本框_字符串_02

双击开始播放,继续双击可以加速播放

右键可以弹出菜单:播放、暂停、退出

左键可以拖动窗口

代码

from tkinter import *
import time

import tkinter as tk

file = "待播放文本.txt"
text=" "

bgcolor = '#000000'
fgcolor = '#FFFFFF'

def getText():
    global text
    # 读
    with open(file, "r",encoding='utf-8') as f:
        # 按字节读
        text = f.read()    
#获取一行
getText()
root = Tk()
# 窗口设定为无边框
root.overrideredirect(True)
# 窗口前置
root.wm_attributes("-topmost", 1)
# 窗口属性 透明度设置
root.attributes("-alpha", 0.8)
# 窗口标题
# root.title("文本播放器")
# 窗口大小
root.geometry("200x35+100+100")
# 更新显示文本
show_str = StringVar(root)
# 初始显示文本
show_str.set("双击播放")
# 源字符
source_str = text
# 播放标记
playflag = True

# 播放位置
pos = 0
# 滚动
def marquee(widget):
    #字符宽度
    textwidth = 18
    # 源字符长度
    strlen = len(source_str)
    # 引用全局变量
    global pos
    # 如果字符长度-播放位置<textwidth
    if strlen - pos < textwidth:
        # 设定显示的字符串为源字符串的(播放位置,播放位置+文本宽度)+ 源字符串的(0,10-字符串长度+播放位置)
        show_str.set(source_str[pos:pos+textwidth] + source_str[0:textwidth - strlen + pos])
    else:
        # 如果大于textwidth,则播放(播放位置,播放位置+文本宽度)的字符
        show_str.set(source_str[pos:pos+textwidth])
    #播放位置+1
    pos += 1
    #如果播放位置大于字符串长度
    if pos > strlen:
        #播放位置设为0
        pos = 0
    # 引用全局变量
    global stopflag
    # 如果当前为播放状态
    if playflag:
        # 睡眠0.3秒后执行滚动函数
        widget.after(300, marquee, widget)
        
# 创建标签
show_lb = Label(root, textvariable=show_str,width=300, fg=fgcolor, bg=bgcolor, text=text, font=("Consolas", 10))
# 设定标签位置
show_lb.place(x=0, y=0, width=200, height=35)

def doubleClicktoPlay(event):
   global playflag
   # 播放
   playflag = True
   marquee(show_lb)

def playStart():
   global playflag
   # 播放
   playflag = True
   marquee(show_lb)
   
def playStop():
   global playflag
   # 暂停播放
   playflag = False

# 创建弹出式菜单
menu = tk.Menu(root, tearoff=0)
# 为菜单添加命令标签
menu.add_command(label="播放", command=playStart) 
menu.add_command(label="暂停", command=playStop)
menu.add_command(label="退出", command=exit)

def popUpMenu(event):
        #在鼠标点击的位置弹出菜单
        menu.post(event.x_root, event.y_root)

# 为消息事件(按键、点击)绑定函数
root.bind_all("<ButtonRelease-3>", popUpMenu) 

def moveStart(event):
    global startX, startY
    #获取鼠标的点击位置的x、y
    startX = event.x
    startY = event.y

def move(event):
     #新坐标=鼠标点击坐标+窗口坐标-初始坐标
    new_x = (event.x) + root.winfo_x() - startX
    new_y = (event.y) + root.winfo_y() - startY
    s = "200x35+" + str(new_x) + "+" + str(new_y)
    # 重新设置窗口大小及其位置
    root.geometry(s)
    
# 为消息事件(按键、点击)绑定函数
root.bind_all("<Button-1>", moveStart)  
root.bind_all("<B1-Motion>", move)
root.bind_all("<Double-Button-1>", doubleClicktoPlay) 
root.mainloop()

注:

如果文本有换行符,切换不会很流畅

可用此方法删除换行符

更新

新增了设定文本、全屏模式、调节大小、保存位置、恢复位置的功能

python 滚动窗口 多列计算 python滚动文本框_窗口大小_03

import time
import tkinter as tk
from tkinter import *
 
file = "playtext.txt"
text=" "
 
bgcolor = '#000000'
fgcolor = '#FFFFFF'
 
def getText():
    global text
    # 读
    with open(file, "r",encoding='utf-8') as f:
        # 按字节读
        text = f.read()
 
#获取一行
getText()
root = Tk()
# 窗口设定为无边框
root.overrideredirect(True)
# 窗口前置
root.wm_attributes("-topmost", 1)
# 窗口属性 透明度设置
root.attributes("-alpha", 0.8)

# 窗口大小,字体
w,h,fs=200,35,10

#得到屏幕宽度
sw = root.winfo_screenwidth()
#得到屏幕高度
sh = root.winfo_screenheight()

def Center(root,w,h):
    # 居中偏移量
    # 需要取整
    dx = int((sw-w) / 2)
    dy = int((sh-h) / 2)
    return str(w) + "x" + str(h) +"+"+str(dx)+"+"+str(dy)

# 窗口大小
root.geometry(Center(root,w,h))
# 更新显示文本
show_str = StringVar(root)
# 初始显示文本
show_str.set("双击播放")
# 源字符
source_str = text
#字符宽度
textwidth = 20
det=textwidth-len(source_str)
if len(source_str) - textwidth < 0:
    print(" "*int(det/2)+source_str+" "*int(det/2))
    source_str=" "*int(det/2)+source_str+" "*int(det/2)

# 播放标记
playflag = True
 
def replay():
    global source_str,pos
    # 从头播放
    pos=0
    # 重新获取文字
    getText()
    source_str = text
    playStop()
    playStart()
    
# 播放位置
pos = 0
# 滚动
def marquee(widget):
    global source_str
    # 源字符长度
    strlen = len(source_str)
    det=textwidth-len(source_str)
    if len(source_str) - textwidth < 0:
        source_str=" "*int(det/2)+source_str+" "*int(det/2)
    strlen = len(source_str)
    # 引用全局变量
    global pos
    # 如果字符长度-播放位置<textwidth
    if strlen - pos < textwidth:
        # 设定显示的字符串为源字符串的(播放位置,播放位置+文本宽度)+ 源字符串的(0,10-字符串长度+播放位置)
        show_str.set(source_str[pos:pos+textwidth] + source_str[0:textwidth - strlen + pos])
    else:
        # 如果大于textwidth,则播放(播放位置,播放位置+文本宽度)的字符
        show_str.set(source_str[pos:pos+textwidth])
    #播放位置+1
    pos += 1
    #如果播放位置大于字符串长度
    if pos > strlen:
        #播放位置设为0
        pos = 0
        #重新播放
        replay()
 
    # 引用全局变量
    global playflag
    # 如果当前为播放状态
    if playflag:
        # 睡眠0.3秒后执行滚动函数
        widget.after(300, marquee, widget)
        
# 创建标签
showlabel = Label(root, textvariable=show_str,width=200, fg=fgcolor, bg=bgcolor, text=text, font=("苹方", 10))
# 设定标签位置
showlabel.place(x=0, y=0, width=200, height=35)
 
def doubleClicktoPlay(event):
   global playflag
   # 播放
   playflag = True
   marquee(showlabel)
 
def playStart():
   global playflag
   # 播放
   playflag = True
   marquee(showlabel)
   
def playStop():
   global playflag
   # 暂停播放
   playflag = False

def setPosition(dx,dy):
    s = str(w) + "x" + str(h) +"+"+ str(dx) + "+" + str(dy)
    root.geometry(s)

def getPosition():
    dx=root.winfo_x()
    dy=root.winfo_y()
    s = str(w) + "x" + str(h) +"+"+ str(dx) + "+" + str(dy)
    # 配置格式:位置 播放速度 
    with open("偏好配置.txt", "w",encoding='utf-8') as f:
        f.write(s+" "+str(fs))

def Preference():
    global root,showlabel,fs,w
    try:
        with open("偏好配置.txt", "r",encoding='utf-8') as f:
            setting=f.readlines()
        settinglist = setting[0].split(" ")
        temp=settinglist[0].split("x")
        w=temp[0]
        print("huifu:"+settinglist[0])
        fs=int(settinglist[1])
        showlabel['font']=("苹方",fs)
        # 设定标签位置
        showlabel.place(x=0, y=0, width=200, height=35)
        root.geometry(settinglist[0])
    except OSError as reason:
        # 如果不处理错误,打包的程序将无法运行      
        # 窗口居中
        s=Center(root,w,h)
        # 窗口大小
        root.geometry(s)
        print('没有配置文件,将使用默认配置\n'+ str(reason))
    
def setSize_old(h):
    global w,fs
    #缩放窗口时,保持左上角位置不变
    x = root.winfo_x() 
    y = root.winfo_y() 
    w = int(h*5.71)
    fs = int(h/2.14)
    global showlabel
    #限制最大和最小尺寸
    if 20<h<4000:
        g=Center(root,w,h)
        #g = f'{w}x{h}+{x}+{y}'
        root.geometry(g)
        showlabel['font']=("苹方",fs)
        showlabel.place(x=0, y=0, width=w, height=h)

def setSize(s,fs):
    # 根据指定参数设定窗口大小
    global showlabel
    root.geometry(s)
    showlabel['font']=("苹方",fs)

def FullScreen():
    getPosition()
    # 创建一个窗口
    global back
    back = tk.Tk()
    # 窗口设定为无边框
    back.overrideredirect(True)
    back.configure(background='black')
    #得到屏幕宽度
    sw = root.winfo_screenwidth()
    #得到屏幕高度
    sh = root.winfo_screenheight()
    # 窗口大小,字体
    w,h=sw,sh
    # 居中偏移量
    # 需要取整
    dx = int((sw-w) / 2)
    dy = int((sh-h) / 2)
    # 窗口大小
    back.geometry(str(w) + "x" + str(h) +"+"+str(dx)+"+"+str(dy))
    tk.Button(back,text='退出全屏',command=notFullScreen).pack()
    setSize_old(407)
    back.mainloop()

def notFullScreen():
    back.destroy()
    Preference()

# 传值变量
tf=tk.StringVar()
# 设置默认值
tf.set("")
def Input():
    # 创建顶级窗口,才可以获取文本框的值
    # 但是顶级窗口上亦可接收鼠标拖动,这是一个bug
    frame = tk.Toplevel()
    frame.title(" ")
    # 创建标签
    L1 = tk.Label(frame,text="键入弹幕:")
    L1.pack(side='left')
    # 创建文本框
    textfield=tk.Entry(frame,bd=5,width=20,textvariable=tf)
    textfield.pack(side='left')
    # 绑定文本框
    textfield.bind('<Return>',ChangeText)
    # 按钮
    button = tk.Button(frame, text="确定")
    # 按钮布局
    button.pack(side='right')
    # 绑定按钮
    button.bind('<Button-1>',ChangeText)
    frame.mainloop()

def ChangeText(event):
    global show_str
    # 接收文本框的值
    res=tf.get()
    print(res)
    if len(res) - textwidth < 0:
        res=" "*int(det/2)+res+" "*int(det/2)
        res=res*20
    with open(file, "w",encoding='utf-8') as f:
            f.write(res)
    replay()
   
# 创建弹出式菜单
menu = tk.Menu(root, tearoff=0)
# 为菜单添加命令标签
menu.add_command(label="设定文本", command=Input)
menu.add_command(label="全屏模式", command=FullScreen)
menu.add_command(label="播放", command=playStart) 
menu.add_command(label="暂停", command=playStop)
menu.add_command(label="保存配置", command=getPosition)
menu.add_command(label="恢复配置", command=Preference)
menu.add_command(label="退出", command=sys.exit)
 
def popUpMenu(event):
        #在鼠标点击的位置弹出菜单
        menu.post(event.x_root, event.y_root)
 
# 为消息事件(按键、点击)绑定函数
root.bind_all("<ButtonRelease-3>", popUpMenu) 
 
def moveStart(event):
    global startX, startY
    #获取鼠标的点击位置的x、y
    startX = event.x
    startY = event.y
 
def move(event):
     #新坐标=鼠标点击坐标+窗口坐标-初始坐标
    new_x = (event.x) + root.winfo_x() - startX
    new_y = (event.y) + root.winfo_y() - startY
    s = str(w) + "x" + str(h) +"+"+ str(new_x) + "+" + str(new_y)
    print(s+" "+str(fs))
    # 重新设置窗口大小及其位置
    root.geometry(s)

w,h,fs=200,35,10
#鼠标滚轮调整窗口大小
def onMouseWheel(event):
    global w, h,fs
    #缩放窗口时,保持左上角位置不变
    x = root.winfo_x() 
    y = root.winfo_y() 
    if event.delta > 0:
        h = int(h*1.05)
        w = int(h*5.71)
        fs = int(h/2.14)
    else:
        h = int(h*0.95)
        w = int(h*5.71)
        fs = int(h/2.14)
    if fs<=10:
        fs=10
    elif fs>300:
        fs=300
    global showlabel
    print(w,h,fs)
    #限制最大和最小尺寸
    if 20<h<4000:
        g = f'{w}x{h}+{x}+{y}'
        root.geometry(g)
        showlabel['font']=("苹方",fs)
        showlabel.place(x=0, y=0, width=w, height=h)
    elif h<20:
        w,h,fs=130,20,10
    
# 为消息事件(按键、点击)绑定函数
root.bind_all("<Button-1>", moveStart)  
root.bind_all("<B1-Motion>", move)
root.bind_all("<Double-Button-1>", doubleClicktoPlay)
root.bind_all("<MouseWheel>",onMouseWheel)
root.mainloop()

弹幕版

移除了读写文件的操作,需手动设定文本,优化了对短文本的播放

import time
import tkinter as tk
from tkinter import *
 
text="双击播放"
 
bgcolor = '#000000'
fgcolor = '#FFFFFF'

root = Tk()
# 窗口设定为无边框
root.overrideredirect(True)
# 窗口前置
root.wm_attributes("-topmost", 1)
# 窗口属性 透明度设置
root.attributes("-alpha", 0.8)

# 窗口大小,字体
w,h,fs=200,35,10

#得到屏幕宽度
sw = root.winfo_screenwidth()
#得到屏幕高度
sh = root.winfo_screenheight()

def Center(root,w,h):
    # 居中偏移量
    # 需要取整
    dx = int((sw-w) / 2)
    dy = int((sh-h) / 2)
    return str(w) + "x" + str(h) +"+"+str(dx)+"+"+str(dy)

# 窗口大小
root.geometry(Center(root,w,h))
# 更新显示文本
show_str = StringVar(root)
# 初始显示文本
show_str.set("双击播放")
# 源字符
source_str = text
#字符宽度
textwidth = 20
det=textwidth-len(source_str)
if len(source_str) - textwidth < 0:
    print(" "*int(det/2)+source_str+" "*int(det/2))
    source_str=" "*int(det/2)+source_str+" "*int(det/2)

# 播放标记
playflag = True
 
def replay():
    global source_str,pos
    # 从头播放
    pos=0
    # 重新获取文字
    source_str = text
    playStop()
    playStart()
    
# 播放位置
pos = 0
# 滚动
def marquee(widget):
    global source_str
    # 源字符长度
    strlen = len(source_str)
    det=textwidth-len(source_str)
    if len(source_str) - textwidth < 0:
        source_str=" "*int(det/2)+source_str+" "*int(det/2)
    strlen = len(source_str)
    # 引用全局变量
    global pos
    # 如果字符长度-播放位置<textwidth
    if strlen - pos < textwidth:
        # 设定显示的字符串为源字符串的(播放位置,播放位置+文本宽度)+ 源字符串的(0,10-字符串长度+播放位置)
        show_str.set(source_str[pos:pos+textwidth] + source_str[0:textwidth - strlen + pos])
    else:
        # 如果大于textwidth,则播放(播放位置,播放位置+文本宽度)的字符
        show_str.set(source_str[pos:pos+textwidth])
    #播放位置+1
    pos += 1
    #如果播放位置大于字符串长度
    if pos > strlen:
        #播放位置设为0
        pos = 0
 
    # 引用全局变量
    global playflag
    # 如果当前为播放状态
    if playflag:
        # 睡眠0.3秒后执行滚动函数
        widget.after(300, marquee, widget)
        
# 创建标签
showlabel = Label(root, textvariable=show_str,width=200, fg=fgcolor, bg=bgcolor, text=text, font=("苹方", 10))
# 设定标签位置
showlabel.place(x=0, y=0, width=200, height=35)
 
def doubleClicktoPlay(event):
   global playflag
   # 播放
   playflag = True
   marquee(showlabel)
 
def playStart():
   global playflag
   # 播放
   playflag = True
   marquee(showlabel)
   
def playStop():
   global playflag
   # 暂停播放
   playflag = False

def setPosition(dx,dy):
    s = str(w) + "x" + str(h) +"+"+ str(dx) + "+" + str(dy)
    root.geometry(s)

def getPosition():
    dx=root.winfo_x()
    dy=root.winfo_y()
    s = str(w) + "x" + str(h) +"+"+ str(dx) + "+" + str(dy)
    # 配置格式:位置 播放速度 
    with open("偏好配置.txt", "w",encoding='utf-8') as f:
        f.write(s+" "+str(fs))

def Preference():
    global root,showlabel,fs,w
    try:
        with open("偏好配置.txt", "r",encoding='utf-8') as f:
            setting=f.readlines()
        settinglist = setting[0].split(" ")
        temp=settinglist[0].split("x")
        w=temp[0]
        print("huifu:"+settinglist[0])
        fs=int(settinglist[1])
        showlabel['font']=("苹方",fs)
        # 设定标签位置
        showlabel.place(x=0, y=0, width=200, height=35)
        root.geometry(settinglist[0])
    except OSError as reason:
        # 如果不处理错误,打包的程序将无法运行      
        # 窗口居中
        s=Center(root,w,h)
        # 窗口大小
        root.geometry(s)
        print('没有配置文件,将使用默认配置\n'+ str(reason))
    
def setSize_old(h):
    global w,fs
    #缩放窗口时,保持左上角位置不变
    x = root.winfo_x() 
    y = root.winfo_y() 
    w = int(h*5.71)
    fs = int(h/2.14)
    global showlabel
    #限制最大和最小尺寸
    if 20<h<4000:
        g=Center(root,w,h)
        #g = f'{w}x{h}+{x}+{y}'
        root.geometry(g)
        showlabel['font']=("苹方",fs)
        showlabel.place(x=0, y=0, width=w, height=h)

def setSize(s,fs):
    # 根据指定参数设定窗口大小
    global showlabel
    root.geometry(s)
    showlabel['font']=("苹方",fs)

def FullScreen():
    getPosition()
    # 创建一个窗口
    global back
    back = tk.Tk()
    # 窗口设定为无边框
    back.overrideredirect(True)
    back.configure(background='black')
    #得到屏幕宽度
    sw = root.winfo_screenwidth()
    #得到屏幕高度
    sh = root.winfo_screenheight()
    # 窗口大小,字体
    w,h=sw,sh
    # 居中偏移量
    # 需要取整
    dx = int((sw-w) / 2)
    dy = int((sh-h) / 2)
    # 窗口大小
    back.geometry(str(w) + "x" + str(h) +"+"+str(dx)+"+"+str(dy))
    tk.Button(back,text='退出全屏',command=notFullScreen).pack()
    setSize_old(407)
    back.mainloop()

def notFullScreen():
    back.destroy()
    Preference()

# 传值变量
tf=tk.StringVar()
# 设置默认值
tf.set("")
def Input():
    # 创建顶级窗口,才可以获取文本框的值
    # 但是顶级窗口上亦可接收鼠标拖动,这是一个bug
    frame = tk.Toplevel()
    frame.title(" ")
    # 创建标签
    L1 = tk.Label(frame,text="键入弹幕:")
    L1.pack(side='left')
    # 创建文本框
    textfield=tk.Entry(frame,bd=5,width=20,textvariable=tf)
    textfield.pack(side='left')
    # 绑定文本框
    textfield.bind('<Return>',ChangeText)
    # 按钮
    button = tk.Button(frame, text="确定")
    # 按钮布局
    button.pack(side='right')
    # 绑定按钮
    button.bind('<Button-1>',ChangeText)
    frame.mainloop()

def ChangeText(event):
    global show_str,text,source_str
    # 接收文本框的值
    res=tf.get()
    print(res)
    if len(res) - textwidth < 0:
        res=" "*int(det/2)+res+" "*int(det/2)
        res=res*20
    text=res
    source_str=res
    replay()
   
# 创建弹出式菜单
menu = tk.Menu(root, tearoff=0)
# 为菜单添加命令标签
menu.add_command(label="设定文本", command=Input)
menu.add_command(label="全屏模式", command=FullScreen)
menu.add_command(label="播放", command=playStart) 
menu.add_command(label="暂停", command=playStop)
menu.add_command(label="保存配置", command=getPosition)
menu.add_command(label="恢复配置", command=Preference)
menu.add_command(label="退出", command=sys.exit)
 
def popUpMenu(event):
        #在鼠标点击的位置弹出菜单
        menu.post(event.x_root, event.y_root)
 
# 为消息事件(按键、点击)绑定函数
root.bind_all("<ButtonRelease-3>", popUpMenu) 
 
def moveStart(event):
    global startX, startY
    #获取鼠标的点击位置的x、y
    startX = event.x
    startY = event.y
 
def move(event):
     #新坐标=鼠标点击坐标+窗口坐标-初始坐标
    new_x = (event.x) + root.winfo_x() - startX
    new_y = (event.y) + root.winfo_y() - startY
    s = str(w) + "x" + str(h) +"+"+ str(new_x) + "+" + str(new_y)
    print(s+" "+str(fs))
    # 重新设置窗口大小及其位置
    root.geometry(s)

w,h,fs=200,35,10
#鼠标滚轮调整窗口大小
def onMouseWheel(event):
    global w, h,fs
    #缩放窗口时,保持左上角位置不变
    x = root.winfo_x() 
    y = root.winfo_y() 
    if event.delta > 0:
        h = int(h*1.05)
        w = int(h*5.71)
        fs = int(h/2.14)
    else:
        h = int(h*0.95)
        w = int(h*5.71)
        fs = int(h/2.14)
    if fs<=10:
        fs=10
    elif fs>300:
        fs=300
    global showlabel
    print(w,h,fs)
    #限制最大和最小尺寸
    if 20<h<4000:
        g = f'{w}x{h}+{x}+{y}'
        root.geometry(g)
        showlabel['font']=("苹方",fs)
        showlabel.place(x=0, y=0, width=w, height=h)
    elif h<20:
        w,h,fs=130,20,10
    
# 为消息事件(按键、点击)绑定函数
root.bind_all("<Button-1>", moveStart)  
root.bind_all("<B1-Motion>", move)
root.bind_all("<Double-Button-1>", doubleClicktoPlay)
root.bind_all("<MouseWheel>",onMouseWheel)
root.mainloop()