GUI的最终选择 Tkinter(五):Text用法
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用法的更多相关文章
- GUI的最终选择Tkinter模块初级篇
一.Tkinter模块的基本使用 1)实例化窗口程序 import tkinter as tk app = tk.Tk() app.title("FishC Demo") app. ...
- Python之GUI的最终选择(Tkinter)
首先,Tkinter是Python默认的GUI库,想IDLE就是用Tkinter设计出来的,因此直接导入Tkinter模块就可以啦 1 import tkinter (1)Tkinter初体验: 1 ...
- GUI的最终选择 Tkinter(七):菜单Menu组件、Menubutton组件、OptionMenu组件
Menu组件 今天说的Menu组件就是一些菜单组件,开始点点点... Tkinter提供了一个Menu组件,可以实现顶级菜单,下拉菜单和弹出菜单.由于底层是代码实现和优化的,所以不太建议通过按钮和其他 ...
- GUI的最终选择 Tkinter(四):Entry、Listbox、Scrollbar和Scale组件
Entry组件 Entry组件就是平时所说的输入框.输入框是程序员用到的最多的一个程序,例如在输入账号和密码的时候需要提供两个输入框,用于接收密码的输入框还会有星号将实际输入的内容隐藏起来. Tkin ...
- GUI的最终选择 Tkinter(三):Checkbutton组件和Radiobutton组件、LabelFrame组件
Checkbutton组件 Checkbutton组件就是常见的多选按钮,而Radiobutton则是单选按钮 from tkinter import * root = Tk() v = IntVar ...
- GUI的最终选择 Tkinter(一):Tkinter最初体验
EasyGui就是一个简单的文字交互界面模块,从今天开始来开始学习Tkinter Tkinter是Python标准的Gui库,它实际是建立在Tk技术上的,Tk最初是为Tcl(一门工具名语言)所涉及的, ...
- GUI的最终选择 Tkinter(九):事件
Tkinter事件处理 Tkinter应用会花费大部分的时间在处理事件循环中(通过mainloop()方法进入),事件可以是触发的鼠标,键盘的操作,管理窗口触发的重绘事件(在多数情况下都是有用户间接引 ...
- GUI的最终选择 Tkinter(八):Message组件、Spinbox组件、PanedWindow组件、Toplevel组件
Message组件 Message(消息)组件是Label组件的变体,用于显示多行文本消息,Message组件能够自动执行,并调整文本的尺寸使其适应给定的尺寸. from tkinter import ...
- GUI的最终选择Tkinter模块练习篇
一.Canvas画布练习 1)简单的绘制图框 from tkinter import * # 构建一个窗口 tk = Tk() # 画布 canvas= Canvas(tk,width=,height ...
随机推荐
- 【转】Pro Android学习笔记(三十):Menu(1):了解Menu
目录(?)[-] 创建Menu MenuItem的属性itemId MenuItem的属性groupId MenuItem的属性orderId MenuItem的属性可选属性 Menu触发 onOpt ...
- ES6学习之数值扩展
二进制和八进制表示法(二进制用前缀0b(或0B)表示,八进制用前缀0o(或0O)表示) Number('0b111') Number('0o10') Number.isFinite()(判断一个值是否 ...
- javascript之模拟块级作用域
在java.C++等语言中,变量i在会在for循环的语句块中定义,循环一旦结束,变量i就会被销毁.可是在javaScript中,从定义开始,就可以在函数内部随处访问.比如 function outpu ...
- 布尔类型(Boolean)
布尔类型(Boolean) 布尔类型仅包含真假,与Python不同的是其首字母小写. == 比较值相等 != 不等于 === 比较值和类型相等 !=== 不等于 || ...
- SSM框架集成Redis
SSM-Spring+SpringMVC+Mybatis框架相信大家都很熟悉了,但是有的时候需要频繁访问数据库中不变或者不经常改变的数据,就会加重数据库的负担,这时我们就会想起Redis Redis是 ...
- C++11新标准
1. 新类型 long long和unsigned long long: char16_t 和 char32_t: 新增原始字符串: 2. 统一的初始化 C++11扩大了用大括号括起的列表(初始化列表 ...
- c# 创建缩略图
/// <summary> /// 创建缩略图 /// </summary> /// <param name="srcFileName">< ...
- 在Visual Studio开发的项目中引用GAC中的dll
Open the windows Run dialog (Windows Key + r) Type C:\Windows\assembly\gac_msil. This is some sort o ...
- 21. 从一道CTF靶机来学习mysql-udf提权
这次测试的靶机为 Raven: 2 这里是CTF解题视频地址:https://www.youtube.com/watch?v=KbUUn3SDqaU 此次靶机主要学习 PHPMailer 跟 mymq ...
- Ubuntu12.04安装svn1.8
先在终端执行sudo sh -c 'echo "# WANdisco Open Source Repo" >> /etc/apt/sources.list.d/WANd ...