Text组件

绘制单行文本使用Label组件,多行选使用Listbox,输入框使用Entry,按钮使用Button组件,还有Radiobutton和Checkbutton组件用于提供单选或多选的情况,多个组件可以使用Frame组件先搭建一个框架,这样组合起来显示好看点,最后还学习了Scrollbar和Scale,Scrollbar组件用于实现滚动条,而Scale则是让用户在一个范围内选择一个确定的值。

Text(文本)组件用于显示和处理多行文本。在Tkinter的所有组件中,Text组件显得异常强大和灵活,它适用于处理多种任务,虽然该组件的蛀牙牡蛎是显示多行文本,但它长城被用于作为简单的文本编辑器和网页浏览器使用。

当创建一个Text组件的时候,它里面是没有内容的,为了给其插入内容,可以利用insert()方法以及INSERT或END索引号:

1 from tkinter import *
2
3 root = Tk()
4 text = Text(root,width=20,height=15)
5 text.pack()
6 text.insert(INSERT,"Python3 \n") #INSERT索引表示插入光标当前的位置
7 text.insert(END,"python算法")
8 mainloop()

执行结果:

Text组件不仅支持插入和编辑文本,它还支持插入image对象和windows组件。

 1 from tkinter import *
2
3 root = Tk()
4 text = Text(root,width=20,height=15)
5 text.pack()
6 text.insert(INSERT,"I love Python3") #INSERT索引表示插入光标当前的位置
7 def show():
8 print("被点了一下。。。")
9 b1 = Button(text,text="点我",command=show)
10 text.window_create(INSERT,window=b1)
11 mainloop()

执行结果:

下面代码将实现单击显示一张图片

 1 from tkinter import *
2
3 root = Tk()
4 text = Text(root,width=20,height=15)
5 text.pack()
6 photo = PhotoImage(file='bg.gif')
7 def show():
8 text.image_create(END,image=photo)
9 b1 = Button(text,text="点我",command=show)
10 text.window_create(INSERT,window=b1)
11 mainloop()

执行结果:

Indexes用法

Indexes(索引)是用来指向Text组件中文本的位置,跟Python的序列索引一样,Text组件索引也是对应实际字符之间的位置。

Tkinter提供一系列不同的索引类型:

  • “line.column”(行/列)
  • “line.end”(某一行的末尾)
  • INSERT
  • CURRENT
  • END
  • user-defined marks
  • user-defined tags("tag.first","tag.last")
  • selection(SELFIRST.SELLAST)
  • window coordinate("@x,y")
  • embedded object name(window,images)
  • expressions

1. "line.column‘’

用行号和列号组成的字符串是常用的索引方法,它们将索引位置的行号和列号以字符串的形式表示出来(中间以“.”分隔,例如“1.0”)。需要注意的是:行号以1开始,列号则以0开始,还可以使用一下语法建索引:

“%d.%d”%(line,column)

指定超出现有文本的最后一行的行号,或超出一行中列数的列号都不会引发错误。对于这样的指定,Tkinter解释为已有内容的末尾的下一个位置。

需要注意的是,使用“行/列”的索引方式看起来像是浮点值,其实在需要指定索引的时候使用浮点值代替也是可以的:

 from tkinter import *

 root = Tk()
text = Text(root,width=20,height=15)
text.pack()
def show():
text.insert(INSERT, "i love python")
print(text.get("1.2", 1.6))
b1 = Button(text,text="点我",command=show)
text.window_create(INSERT,window=b1)

执行结果:

2. “line.end”

行号加上字符串“.end”的格式表示该行最后一个字符串的位置:

from tkinter import *

root = Tk()
text = Text(root,width=20,height=15)
text.pack()
def show():
text.insert(INSERT, "i love python")
print(text.get("1.2", "1.end"))
b1 = Button(text,text="点我",command=show)
text.window_create(INSERT,window=b1) mainloop()

执行结果:

3. INSERT(或“insert”)

对应插入光标的位置

4. CURRENT(或“current”)

对应与鼠标坐标最接近的位置。不过,如果你紧按鼠标任何一个按钮,会直接到你松开才相应。

5. END(或“end”)

对应Text组件的文本缓冲区最后一个字符的下一个位置。

6. user-defined marks

user-defined marks是对Text组件中位置的命名。INSERT和CURRENT是两个预先命名好的marks,除此之外可以自定义marks。

7. User-defined tags

User-defined tags代表可以分配给Text组件的特殊事件绑定和风格。、

可以使用“tag.first”(使用tag的未必能是第一个字符之间)和“tag.last”(使用tag的文本的最后一个字符之后)语法表示标签的范围:

“%s.first”%tagname 

“%s.last”%tagnam 

8. selection(SELFIRST,SELLAST)

selection是一个名为SEL(或“sel”)的特殊tag,表示当前被选中的范围,可以使用SELFIRST和SELLAST来表示这个范围如果没有选中的内容,那么Tkinter会抛出一个TclError异常。

9. windows coordinate("@x.y")

可以使用串口坐标作为索引。例如在一个时间绑定中,你可以使用以下代码找到最接近的鼠标字符:

“@%d,%d”%(event.x,event.y)

10. embedded object name(window,images)

embedden object name 用于指向在Text组件中嵌入的window和image对象。要引用一个window,只要简单地讲一个Tkinter组件实例作为索引即可。引用一个嵌入的image,只需要使用相应的PhotoImage和BitmapImage对象。

11. expressions

expressions用于修改任何格式的索引,用字符串的形式实现修改索引的表达式,具体表达式实现如表:

表达式 含义
"+count chars" 将索引向前(->)移动count个字符。可以越过换行符,但不能超过END的位置
"-count chars" 将索引向后(<-)移动count个字符。可以越过换行符,但不能超过"1.0"的位置
"+count lines" 将索引向前(->)移动count行,索引会尽量保持与移动前在同一列上,但如果移动后的那一行字符太少,将移动到该行的末尾。
"-count lines" 将索引向后(,-)移动count行,索引会尽量保持与移动前在同一列上,但如果移动后的那一行字符太少,将移动到该行的末尾。
"linestart" 将索引移动到当前索引所在行的起始位置。注意:使用的该表达式前面必须用一个空格隔开
"lineend" 将索引移动到当前索引所在行的末尾位置。注意:使用的该表达式前面必须用一个空格隔开
"wordstart" 将索引移动到当前索引指向的单词的开头。单词的定义是一系列字母、数字、下划线或任何为空白字符的组合。注意:使用该表达式前面必须用一个空格隔开
"wordend" 将索引移动到当前索引指向的单词的末尾。单词的定义是一系列字母、数字、下划线或任何为空白字符的组合。注意:使用该表达式前面必须用一个空格隔开

提示:只要结果不产生歧义,关键字可以被缩写,空格可以是省略的,例如:“+ 5chars”可以缩写为“+5c”

在实现中,为了确保表达式为普通字符串,你可以使用str或格式化操作来创建一个表达式字符串。来看下面列子,删除插入光标前面的一个字符。

def backspace(event):
event.widgt.delete("%s-1c"%INSERT,INSERT)

Marks用法

Marks通常是指嵌入到Text组件文本中的不可见对象,简单的说就是指定字符串间的位置,它会跟着相应的字符一起移动。Marks有INSERT,CURRENT和user-defined marks(用户自定义的Marks)。其中,INSERT和CURRENT是Tkinter预定义的特殊Marks,它不能够被删除。

INSERT(或insert)用于指定当前插入的光标的而为之,Tkinter会在该位置绘制一个闪烁的光标(因此并不是所有的Marks都不可见)。

CURRENT(或current)用于指定与与鼠标坐标最接近的位置。不过,如果你紧按鼠标任何一个按钮,它会知道你松开才响应。

还可以自定义任意数量的Marks,Marks的名字是由普通字符串组成,可以是除了空白字符的任何字符(为了避免歧义,你应该起一个有意义的名字),使用mark_ser()方法创建和移动Marks.

如果在一个Marks标记的位置之前插入或删除文本,那么Marks跟着一并移动,删除Marks需要使用mark_unset()方法,删除Marks周围的文本并并不会删除Marks本身。

其实Marks事实上就是索引,用于表示位置:

 from tkinter import *
root = Tk()
text = Text(root,width=,height=)
text.pack()
text.insert(INSERT,'I love python')
text.mark_set("here","1.2")
text.insert('here',"插")
mainloop()

执行结果:

再如,如果Marks前边的内容发生改变,那么Marks的位置也会跟着移动(实际上,就是Mark会记住它后面的内容)

 from tkinter import *
root = Tk()
text = Text(root,width=,height=)
text.pack()
text.insert(INSERT,'I love python')
text.mark_set("here","1.2")
text.insert('here',"插")
text.insert('here',"入")
mainloop()

执行结果:

再如,如果Marks周围的文本被删了,Mark仍然在:

 from tkinter import *
root = Tk()
text = Text(root,width=,height=)
text.pack()
text.insert(INSERT,'I love python')
text.mark_set("here","1.2")
text.insert('here',"插")
text.delete('1.0',END)
text.insert('here',"入")
mainloop()

执行结果:

再如,只有mark_unset()方法可以解除Mark的封印;

 from tkinter import *
root = Tk()
text = Text(root,width=,height=)
text.pack()
text.insert(INSERT,'I love python')
text.mark_set("here","1.2")
text.insert('here',"插")
text.mark_unset("here")
text.delete('1.0',END)
text.insert('here',"入")
mainloop()

错误信息:

---------------------------------------------------------------------------
TclError Traceback (most recent call last)
<ipython-input--2c32c5656dea> in <module>()
text.mark_unset("here")
text.delete('1.0',END)
---> text.insert('here',"入")
mainloop() c:\python36\lib\tkinter\__init__.py in insert(self, index, chars, *args)
"""Insert CHARS before the characters at INDEX. An additional
tag can be given in ARGS. Additional CHARS and tags can follow in ARGS."""
-> self.tk.call((self._w, 'insert', index, chars) + args)
def mark_gravity(self, markName, direction=None):
"""Change the gravity of a mark MARKNAME to DIRECTION (LEFT or RIGHT). TclError: bad text index "here"

默认插入内容到Mark,是插入到他的左侧(就是说插入一个字符的话,Mark向后移动了一个字符的位置)。那么能不能插入到Mark的右侧了?答案是可以的,通过mark_gravity()方法就可以实现。

Tags用法

Tags(标签)通常用于改变Text组件中内容的样式和功能。可以用来修改文本的字体,尺寸和颜色。另外,Tags还允许将文本,嵌入的组件和图片与键盘鼠标等事件相关联。除了user-defined tags(用户自定义的Tags),还有一个预定义的特殊Tags:SEL.

SEL(或sel)用于表示对象的选中内容(如果有的话)

可以自定义任意数量的Tags,Tags的名字是有普通字符串组成,可以是除了空白字符以外的额任意字符。另外,任何文本内容都支持多个Tags描述,任何Tags也可以用于描述多个不同的文本内容。

为指定文本添加Tags可以使用tag_add()方法:

 from tkinter import *
root = Tk()
text = Text(root,width=,height=)
text.pack()
text.insert(INSERT,'I love python3.6')
text.tag_add("tag1","1.7","1.12","1.14")
text.tag_config("tag1",background="yellow",foreground="red")
mainloop()

执行结果:

如上,使用tag_config()方法可以设置Tags的样式。下表列举了tag_config()可以使用的选项。

如上,使用 tag_config() 方法可以设置 Tags 的样式。下面罗列了 tag_config() 方法可以使用的选项:

选项 含义
background

①指定该Tag所描述的内容的背景颜色

②注意:bg并不是该选项的缩写,在这里bg被解释成bgstipple选项的缩写

bgstipple

①指定一个位图作为背景,并使用background选项指定的颜色填充

②只有设定了background选项,该选项才会生效

③默认的标准位图有:'error', 'gray75', 'gray50', 'gray25', 'gray12', 'hourglass', 'info', 'questhead', 'question'和'warning'

borderwidth

①指定文本框的宽度

②默认值是0

③只有设定了relief选项该选项才会生效

④注意:该选项不能使用bd缩写

fgstipple

①指定一个位图作为前景色

②默认的标准位图有:'error', 'gray75', 'gray50', 'gray25', 'gray12', 'hourglass', 'info', 'questhead', 'question'和'warning'

font ①指定该Tag所描述的内容使用的字体
foreground

①指定该Tag所描述的内容使用的前景色

②注意:fg并不是该选项的缩写,在这里fg被解释为fgstipple的缩写

justify

①控制文本的对齐方式

②默认是LEFT(左对齐),还可以选择RIGHT(右对齐)和CENTER(居中)

③注意:需要将Tag指向该行的第一个字符,该选项才能生效

Imargin1

①设置Tag指向的文本块第一行的缩进

②默认值是0

③注意:需要将Tag指向该行的第一个字符或整个文本块,该选项才能生效

Imargin2

①设置Tag指向的文本块除了第一行其他行的缩进

②默认值是0

③注意:需要将Tag指向整个文本块,该选项才能生效

offset

①设置Tag指向的文本相对于基线的偏移距离

②可以控制文本相对于基线是升高(正数值)或者降低(负数值)

③默认值是0

overstrike

①在Tag指定的文本范围画一条删除线

②默认值是False

relief

①指定Tag对应范围的文本的边框样式

②可以使用的值有:SUNKEN,RAISED,GROOVE,RIDGE或FLAT

③默认值是FLAT(没有边框)

margin

①设置Tag指向的文本块右侧的缩进

②默认值是0

spacing1

①设置Tag所描述的文本块中每一行与上方的文本间隔

②注意:自动换行不算

③默认值是0

spacing2

①设置Tag所描述的文本块中自动换行的各行间的空白间隔

②注意:换行符("\n")不算

③默认值是0

spacing3

①设置Tag所描述的文本块中每一行与下方的文本间隔

②注意:自动换行不算

③默认值是0

tabs

①定制Tag所描述的文本块中Tab按键的功能

②默认Tab被定义为8个字符的宽度

③你还可以定制多个制表位:tabs=('3c', '5c', '12c')表示前三个Tab的宽度分别为3cm,5cm,12cm,接着的Tab按照最后两个的差值计算,即:19cm,26cm,33cm

④你应该注意到,它上边'c'的含义是“厘米”而不是“字符”,还可以选择的单位有"i"(英寸),"m"(毫米),"p"(DPI,大约是'1i'等于'72p')

⑤如果是一个整型值,则单位是像素

underline

①该选项设置为True的话,则Tag所描述的范围内的文本将被画上下划线

②默认值是False

wrap

①设置当一行文本的长度超过width选项设置的宽度时,是否自动换行。

②该选项的值可以是:NONE(不自动换行),CHAR(按字符自动换行)和WORD(按单词自动换行)

如果对于一个范围内容的文本加上多个Tags,并且相同的选项,那么新创建的Tag样式会覆盖比较旧的Tag:

 from tkinter import *
root = Tk()
text = Text(root,width=,height=)
text.pack()
text.tag_config("tag1",background="yellow",foreground="red")
text.tag_config("tag2",foreground="blue")
#tag2中foreground将覆盖tag1中的foreground,tag2中没有background,所以用tag1的
text.insert(INSERT,'I love python3.6',("tag1","tag2"))
mainloop()

执行结果:

不过,这里可以使用tagraise()和tag_lower()方法来提高和降低某个Tag的优先级:

 from tkinter import *
root = Tk()
text = Text(root,width=,height=)
text.pack()
text.tag_config("tag1",background="yellow",foreground="red")
text.tag_config("tag2",foreground="blue")
text.tag_lower("tag1")
text.insert(INSERT,'I love python3.6',("tag1","tag2"))
mainloop()

执行结果:

Tag还可以支持时间的绑定,绑定时间使用的是tag_bind()方法。下面举个例子:让文本(“python3.6”)与鼠标事件进行绑定,当鼠标进入该文本段的时候,鼠标样式切换“arrow”状态,离开文本段的时候切换回“xterm”形态。当触发鼠标“左键单击操作”时间的时候,使用默认浏览器打开绑定的链接:

 from tkinter import *
root = Tk()
text = Text(root,width=,height=)
text.pack()
text.insert(INSERT,'I love python3.6')
text.tag_add("link","1.7","1.16")
text.tag_config("link",foreground="blue",underline=True)
def show_hand_cursor(event):
text.config(cursor="arrow")
def show_arrow_cursor(event):
text.config(cursor="xterm")
def click(event):
webbrowser.open("http://www.baidu.com")
text.tag_bind("link","<Enter>",show_hand_cursor)
text.tag_bind("link","<Leave>",show_arrow_cursor)
text.tag_bind("link","<Button-1>",click)
mainloop()

执行结果:

下面给大家介绍几个比较实用的Text组件

第一个是判断内容是否发生变化,例如做一个记事本,当用户关闭的时候,程序需要检查内容是否有改动,如果有,需要提醒用户保存。下面的例子中,可以通过小燕Text组件中文本的MD5摘要来判断内筒是否发生改变。

 from tkinter import *
import hashlib
root = Tk()
text = Text(root,width=,height=)
text.pack()
text.insert(INSERT,"I love Python3.x")
contents = text.get(1.0,END)
def getSig(contents):
m = hashlib.md5(contents.encode())
return m.digest()
sig = getSig(contents)
def check():
contents = text.get(1.0,END)
if sig != getSig(contents):
print("内容没有保存")
else:
print("文件没有改动")
Button(root,text="检查",command=check).pack()
mainloop()

执行结果:

第二个是查找操作,是使用search()方法可以搜索Text组件中的内容,可以提供一个确切的目标进行搜索(默认),也可以使用Tcl格式的正则表达式进行搜索需要设置regexp选项为True(:):

 from tkinter import*
root = Tk()
text = Text(root,width=,height=)
text.pack()
text.insert(INSERT,"I love python3.6")
#将任何格式的索引号统一为元组(行,列)的格式输出
def getIndex(text,index):
return tuple(map(int,str.split(text.index(index),".")))
start = 1.0
while True:
pos = text.search("o",start,stopindex=END)
if not pos:
break
print("找到了,位置是:",getIndex(text,pos))
start = pos + "+1c" #将start指向下一个字符
mainloop()

执行结果:

找到了,位置是: (1, 3)
找到了,位置是: (1, 11)
这里第一是找出“o"的位置,然后以"o"为起点,继续找第二个位置,直到找完
注意:这里如果忽略stopindex选项,表示知道文本的末尾结束搜索。设置backwards选项为True,则是修改搜索的方向(变为向后搜索,那么start遍历应该设置为END,stopindex选项设置为1.0,最后+1c设置为-1c )
最后,Text组件还支持“恢复”和“撤销”操作,这使得Text组件显得相当高大上。通过设置undo选项为True,可以开启Text组件的撤销功能,然后利用editundo可以开启实现撤销操作,用editredo()方法可以实现恢复操作。
 from tkinter import *
import hashlib
root = Tk()
text = Text(root,width=,height=)
text.pack()
text.insert(INSERT,"I love Python3.x")
def show():
text.edit_undo()
Button(root,text="撤销",command=show).pack()
mainloop()
执行结果:

点击一次撤销后,插入进去的数据已经没有 了

这是因为Text组件内部有一个栈专门用于记录每次变动,所以每次“撤销”操作就是一次弹栈操作,“恢复”就是再次压栈。

默认情况下,每次完整的操作都会放入栈中。但怎么样算是一次完整的操作了?Tkinter觉得每次焦点切换、用户按下回车键、删除/插入操作的转换等之前的操作算是一次完整的操作。也就是说,你连续输入“python”的话,一次“撤销”操作就会将所有内容删除。

但是我们可以自定义,做法就是先将autoseparators选项设置为False(因为这个选项是让Tkinter在人为一次完成的操作结束后自动插入“分隔符”,然后绑定键盘事件,每次有输入就用edit_separator()方法人为插入一个“分隔符”:

 from tkinter import *
import hashlib
root = Tk()
text = Text(root,width=20,height=5,autoseparators=False,undo=True,maxundo=10) text.pack()
def callback():
text.edit_separator()
text.bind("<Key>",callback)
text.insert(INSERT,"I love Python3.x")
def show():
text.edit_undo()
Button(root,text="撤销",command=show).pack()
mainloop()
												

GUI的最终选择 Tkinter(五):Text用法的更多相关文章

  1. GUI的最终选择Tkinter模块初级篇

    一.Tkinter模块的基本使用 1)实例化窗口程序 import tkinter as tk app = tk.Tk() app.title("FishC Demo") app. ...

  2. Python之GUI的最终选择(Tkinter)

    首先,Tkinter是Python默认的GUI库,想IDLE就是用Tkinter设计出来的,因此直接导入Tkinter模块就可以啦 1 import tkinter (1)Tkinter初体验: 1 ...

  3. GUI的最终选择 Tkinter(七):菜单Menu组件、Menubutton组件、OptionMenu组件

    Menu组件 今天说的Menu组件就是一些菜单组件,开始点点点... Tkinter提供了一个Menu组件,可以实现顶级菜单,下拉菜单和弹出菜单.由于底层是代码实现和优化的,所以不太建议通过按钮和其他 ...

  4. GUI的最终选择 Tkinter(四):Entry、Listbox、Scrollbar和Scale组件

    Entry组件 Entry组件就是平时所说的输入框.输入框是程序员用到的最多的一个程序,例如在输入账号和密码的时候需要提供两个输入框,用于接收密码的输入框还会有星号将实际输入的内容隐藏起来. Tkin ...

  5. GUI的最终选择 Tkinter(三):Checkbutton组件和Radiobutton组件、LabelFrame组件

    Checkbutton组件 Checkbutton组件就是常见的多选按钮,而Radiobutton则是单选按钮 from tkinter import * root = Tk() v = IntVar ...

  6. GUI的最终选择 Tkinter(一):Tkinter最初体验

    EasyGui就是一个简单的文字交互界面模块,从今天开始来开始学习Tkinter Tkinter是Python标准的Gui库,它实际是建立在Tk技术上的,Tk最初是为Tcl(一门工具名语言)所涉及的, ...

  7. GUI的最终选择 Tkinter(九):事件

    Tkinter事件处理 Tkinter应用会花费大部分的时间在处理事件循环中(通过mainloop()方法进入),事件可以是触发的鼠标,键盘的操作,管理窗口触发的重绘事件(在多数情况下都是有用户间接引 ...

  8. GUI的最终选择 Tkinter(八):Message组件、Spinbox组件、PanedWindow组件、Toplevel组件

    Message组件 Message(消息)组件是Label组件的变体,用于显示多行文本消息,Message组件能够自动执行,并调整文本的尺寸使其适应给定的尺寸. from tkinter import ...

  9. GUI的最终选择Tkinter模块练习篇

    一.Canvas画布练习 1)简单的绘制图框 from tkinter import * # 构建一个窗口 tk = Tk() # 画布 canvas= Canvas(tk,width=,height ...

随机推荐

  1. 【转】Pro Android学习笔记(三十):Menu(1):了解Menu

    目录(?)[-] 创建Menu MenuItem的属性itemId MenuItem的属性groupId MenuItem的属性orderId MenuItem的属性可选属性 Menu触发 onOpt ...

  2. ES6学习之数值扩展

    二进制和八进制表示法(二进制用前缀0b(或0B)表示,八进制用前缀0o(或0O)表示) Number('0b111') Number('0o10') Number.isFinite()(判断一个值是否 ...

  3. javascript之模拟块级作用域

    在java.C++等语言中,变量i在会在for循环的语句块中定义,循环一旦结束,变量i就会被销毁.可是在javaScript中,从定义开始,就可以在函数内部随处访问.比如 function outpu ...

  4. 布尔类型(Boolean)

    布尔类型(Boolean) 布尔类型仅包含真假,与Python不同的是其首字母小写. ==      比较值相等 !=       不等于 ===   比较值和类型相等 !===  不等于 ||   ...

  5. SSM框架集成Redis

    SSM-Spring+SpringMVC+Mybatis框架相信大家都很熟悉了,但是有的时候需要频繁访问数据库中不变或者不经常改变的数据,就会加重数据库的负担,这时我们就会想起Redis Redis是 ...

  6. C++11新标准

    1. 新类型 long long和unsigned long long: char16_t 和 char32_t: 新增原始字符串: 2. 统一的初始化 C++11扩大了用大括号括起的列表(初始化列表 ...

  7. c# 创建缩略图

    /// <summary> /// 创建缩略图 /// </summary> /// <param name="srcFileName">< ...

  8. 在Visual Studio开发的项目中引用GAC中的dll

    Open the windows Run dialog (Windows Key + r) Type C:\Windows\assembly\gac_msil. This is some sort o ...

  9. 21. 从一道CTF靶机来学习mysql-udf提权

    这次测试的靶机为 Raven: 2 这里是CTF解题视频地址:https://www.youtube.com/watch?v=KbUUn3SDqaU 此次靶机主要学习 PHPMailer 跟 mymq ...

  10. Ubuntu12.04安装svn1.8

    先在终端执行sudo sh -c 'echo "# WANdisco Open Source Repo" >> /etc/apt/sources.list.d/WANd ...