从压缩包/托盘图标笔记本编程
这个程序的想法很简单:将剪贴板上的任何文本保存在一个方便的小托盘图标上,它就会被保存到 SQLite 数据库中。看起来没什么变化,但文本被保存在一个新的记录中。
还可以搜索已经保存的笔记。当您在托盘图标上单击鼠标中键时,会弹出一个窗口。我更希望它在右键单击图标时出现,但右键单击只能显示菜单。这就是右键单击的作用:它们显示上下文菜单。现在,如果您有一个带有滚轮或按钮的鼠标,可以单击中键。对于那些使用笔记本电脑触控板的人来说,中键单击可以通过同时单击左右按钮来模拟。在 KDE 中,系统设置中有一个选项可以禁用此功能,但默认情况下它是启用的。
如果您愿意,可以尝试在应用程序启动时隐藏主窗口,或者不使用 skiptaskbar 属性,这样最小化的主窗口就不会在面板中与其他打开的应用程序一起列出,但这些效果并不吸引人,因此它们保持默认设置。
通过 Google 图片搜索可以找到适合托盘图标的图片
它不需要是任何特定的尺寸。它在程序运行时会很好地自动调整大小。
窗口有一个大的文本区域来显示笔记,以及左上角的一个文本框,用于输入搜索文本。还有三个标签显示保存笔记的日期/时间、笔记在使用该搜索文本找到的所有笔记中的位置,以及为了便于查看,笔记的记录 ID。
托盘图标(左下角)可以放置在任何位置。它不会出现在窗口中。它出现在系统托盘中(通常位于屏幕的右下角,在 KDE 的默认面板中)。
托盘图标有自己的组件,因此请检查项目 > 属性以查看它和数据库组件是否已包含。
总之,要使用此笔记本
- 将任何文本复制到剪贴板。
- 单击托盘图标,您的文本将被保存。
- 同时单击鼠标左键和右键单击托盘图标,以搜索笔记。
有时,保存文本然后打开窗口并添加一些关键词来帮助您再次找到笔记会很有用。我有时会添加 JOKE、QUOTE 或 FAMILY HISTORY 等词语。这样,通过在搜索框中键入“QUOTE”,我所有的引用都会出现,我可以一次查看一个。复制所有选定的笔记将是一个有用的功能。
出于便于查看的目的,SQL select 语句将出现在窗口标题中。
为了确保完善,文本菜单有四个条目用于调整文本
输入更多... 将光标定位在可见笔记文本的末尾,以便您输入更多内容。
整理 将多个空格、多个制表符和多个空行删除,并将开头和结尾的空格删除。
句子 将断句合并。从电子邮件中复制的文本通常有不同的行。
双空格 用空行分隔段落。
这些文本操作在没有选择文本的情况下会对整个文本进行操作,如果有选择文本,则会对选定的文本进行操作。快捷键在那里是因为我经常发现自己会连续快速执行最后三个操作来使文本看起来体面。
其他菜单是
数据库是一个 SQLite 文件。您可能想知道 OPEN 和 NEW 菜单项在哪里。为简单起见,程序在打开时会创建一个新的数据库,如果不存在与它预期名称和位置的数据库。它名为 Notebook.sqlite,它位于 Home 目录中。如果您愿意,可以很容易地更改代码以将其放置在 Documents 目录中。
文件 > 备份 是一个有用的菜单项,它将数据库文件复制到 Documents 文件夹中的 Backups 文件夹,并使用日期戳来显示文件创建的时间,并防止干扰之前的备份。
文件 > 重新编号 使记录 ID 连续,没有间隙。它和下面的 Vacuum 项是多余的。Vacuum 将整理数据库文件的内部结构。它使用 SQLite 的内置 vacuum 命令。
文件 > 退出 关闭应用程序。文件 > 隐藏 使窗口不可见。单击窗口上的关闭框也只会隐藏窗口;它不会关闭应用程序。为了实现此技巧,窗口的 Persistent 属性设置为 true。当单击窗口的关闭框时,窗口会持续存在,但不可见。
当您在搜索文本框中键入时,一个 SQL Select 语句会选择包含该文本的所有笔记。这是一个简单的搜索:它不会搜索包含任何单词的笔记,从最匹配到最不匹配排序。它搜索与键入的文本完全匹配的文本。当您键入每个字母时,都会执行搜索。mdb 模块中的一个公共属性 Public rs As Result 存储搜索结果。笔记 > 显示全部 清除搜索并查找所有笔记。这在程序启动时发生:所有笔记都被选中。
笔记 > 新建... 允许您键入一个新的笔记,当您离开文本区域时该笔记会被保存。
笔记 > 删除 从数据库中删除当前显示的笔记。
笔记 > 清除全部 从数据库中清除所有笔记。没有撤消操作,但有机会退出。
窗口属性是
排列 = 垂直
堆叠 = 上方
宽度 = 800
高度 = 500
需要设置的托盘图标 (ti1) 属性是
工具提示 = 单击以保存剪贴板文本 可见 = True
请务必将其设置为可见,否则您在运行程序时将看不到托盘图标,并且在隐藏最初显示的窗口后,您将无法以优雅的方式退出正在运行的程序。
堆叠 属性确保窗口始终位于其他窗口之上。例如,如果您单击网页浏览器的窗口,笔记本窗口不会被浏览器窗口覆盖,而是会始终位于最上面,漂浮在其之上。这对于实用程序类型的程序来说非常有用。
在查看代码之前,最后要说明的是使用键盘的箭头键。UP 和 DOWN 分别将您带到找到的笔记的第一条和最后一条。LEFT 和 RIGHT 分别将您在找到的笔记中向前或向后移动。“找到的笔记”是指那些包含您在文本框中键入的文本字符串的笔记,或者如果没有键入任何内容,则指所有笔记。
代码后面有一些注释。
与数据库相关的代码收集在一个名为 mdb 的模块中。
' Gambas module file
Public db1 As New Connection
Public rs As Result
Public SQLSel As String
Public Sub CreateDatabase()
db1.Type = "sqlite"
db1.host = User.home
db1.name = ""
'delete an existing Notebook.sqlite
If Exist(User.home & "/Notebook.sqlite") Then
Kill User.home & "/Notebook.sqlite"
Endif
'create Notebook.sqlite
db1.Open
db1.Databases.Add("Notebook.sqlite")
db1.Close
End
Public Sub ConnectDatabase()
db1.Type = "sqlite"
db1.host = User.home
db1.name = "Notebook.sqlite"
db1.Open
SelectAllNotes
Debug("Notes file connected:<br>" & db1.Host &/ db1.Name & "<br><br>" & rs.Count & " records")
End
Public Sub MakeTable()
Dim hTable As Table
db1.name = "Notebook.sqlite"
db1.Open
hTable = db1.Tables.Add("Notes")
hTable.Fields.Add("KeyID", db.Integer)
hTable.Fields.Add("Created", db.Date)
hTable.Fields.Add("Note", db.String)
hTable.PrimaryKey = ["KeyID"]
hTable.Update
Message("Notes file created:<br>" & db1.Host &/ db1.Name)
End
Public Sub SelectAllNotes()
rs = db1.Exec("SELECT * FROM Notes")
FMain.Caption = "SELECT * FROM Notes"
rs.MoveLast
End
Public Sub Massage(z As String) As String
While InStr(z, "''") > 0 'this avoids a build-up of single apostrophes
Replace(z, "''", "'")
Wend
Return Replace(z, "'", "''")
End
Public Sub AddRecord(s As String, t As Date) As String
Dim rs1 As Result
Dim NextID As Integer
If rs.Max = -1 Then NextID = 1 Else NextID = db1.Exec("SELECT Max(KeyID) AS TheMax FROM Notes")!TheMax + 1
db1.Begin
rs1 = db1.Create("Notes")
rs1!KeyID = NextID
rs1!Created = t 'time
rs1!Note = Massage(s)
rs1.Update
db1.Commit
SelectAllNotes
Return NextID
Catch
db1.Rollback
Message.Error(Error.Text)
End
Public Sub UpdateRecord(RecNum As Integer, NewText As String)
db1.Exec("UPDATE Notes SET Note='" & Massage(NewText) & "' WHERE KeyID=" & RecNum)
Dim pos As Integer = rs.Index
'Refresh the result cursor, so the text in it is updated as well as in the database file. This is tricky.
If IsNull(SQLSel) Then rs = db.Exec("SELECT * FROM Notes") Else rs = db.Exec(SQLSel) 'SQLSel is the last search, set by typing in tbSearch
rs.MoveTo(pos) 'Ooooh yes! It did it.
Catch
Message.Error("<b>Update error.</b><br><br>" & Error.Text)
End
Public Sub MoveRecord(KeyCode As Integer) As Boolean
If rs.Count = 0 Then Return False
Select Case KeyCode
Case Key.Left
If rs.Index > 0 Then rs.MovePrevious Else rs.MoveLast
Return True
Case Key.Right
If rs.Index < rs.Max Then rs.MoveNext Else rs.MoveFirst
Return True
Case Key.Up
rs.MoveFirst
Return True
Case Key.Down
rs.MoveLast
Return True
End Select
Return False
End
Public Sub ClearAll()
If Message.Warning("Delete all notes? This cannot be undone.", "Ok", "Cancel") = 1 Then
db1.Exec("DELETE FROM Notes")
SelectAllNotes
Endif
End
Public Sub DeleteRecord(RecNum As Integer)
db1.Exec("DELETE FROM Notes WHERE KeyID='" & RecNum & "'")
SelectAllNotes
End
Public Sub SearchFor(s As String)
SQLSel = "SELECT * FROM Notes WHERE Note LIKE '%" & Massage(s) & "%'"
If IsNull(s) Then
SelectAllNotes
SQLSel = ""
Else
FMain.Caption = SQLSel
rs = db1.Exec(SQLSel)
Endif
End
Public Sub Renumber()
Dim res As Result = db.Exec("SELECT * FROM Notes ORDER BY KeyID")
Dim i As Integer = 1
Dim x As Integer
Application.Busy += 1
While res.Available
x = res!KeyID
db.Exec("UPDATE Notes SET KeyID=" & i & " WHERE KeyID=" & x)
i += 1
res.MoveNext
Wend
SelectAllNotes
Application.Busy -= 1
End
Public Sub Vacuum() As String
Dim fSize1, fSize2 As Float
fSize1 = Stat(db1.Host &/ db1.Name).Size / 1000 'kB
db1.Exec("Vacuum")
fSize2 = Stat(db1.Host &/ db1.Name).Size / 1000 'kB
Dim Units As String = "kB"
If fSize1 > 1000 Then 'megabyte range
fSize1 /= 1000
fSize2 /= 1000
Units = "MB"
Endif
Return Format(fSize1, "#.0") & Units & " -> " & Format(fSize2, "#.0") & Units & " (" & Format(fSize1 - fSize2, "#.00") & Units & ")"
End
Public Sub Backup() As String
If Not Exist(User.Home &/ "Documents/Backups/") Then Mkdir User.Home &/ "Documents/Backups"
Dim fn As String = "Notebook " & Format(Now, "yyyy-mm-dd hh-nn")
Dim source As String = db1.Host &/ db1.Name
Dim dest As String = User.Home &/ "Documents/Backups/" & fn
Try Copy source To dest
If Error Then Return "Couldn't save -> " & Error.Text Else Return "Saved -> /Documents/Backups/" & fn
End
' Gambas class file
Public OriginalText As String
Public Sub ti1_Click()
Dim TimeAdded As String
If Clipboard.Type = Clipboard.Text Then TimeAdded = mdb.AddRecord(Clipboard.Paste("text/plain"), Now())
End
Public Sub Form_Open()
If Not Exist(User.Home &/ "Notebook.sqlite") Then 'create notebook data file
mdb.CreateDatabase
mdb.MakeTable
mdb.SelectAllNotes
Else
mdb.ConnectDatabase
ShowRecord
Endif
End
Public Sub MenuQuit_Click()
mdb.db1.Close
ti1.Delete
Quit
End
Public Sub Form_KeyPress()
If mdb.MoveRecord(Key.Code) Then
ShowRecord
Stop Event
Endif
End
Public Sub ShowRecord()
If mdb.rs.count = 0 Then
ClearFields
Return
Endif
ta1.Text = Replace(mdb.rs!Note, "''", "'")
Dim d As Date = mdb.rs!Created
labTime.text = Format(d, gb.MediumDate) & " " & Format(d, gb.LongTime)
labRecID.text = mdb.rs!KeyID
labLocation.text = Str(mdb.rs.Index + 1) & "/" & mdb.rs.Count
OriginalText = ta1.Text
End
Public Sub MenuClear_Click()
mdb.ClearAll
ClearFields
labTime.Text = "No records"
End
Public Sub MenuCopy_Click()
Clipboard.Copy(ta1.Text)
End
Public Sub MenuDeleteNote_Click()
Dim RecNum As Integer = Val(labRecID.Text)
mdb.DeleteRecord(RecNum) 'after which all records selected; now to relocate...
Dim res As Result = db.Exec("SELECT * FROM Notes WHERE KeyID<" & RecNum)
res.MoveLast
Dim i As Integer = res.Index
mdb.rs.MoveTo(i)
ShowRecord
End
Public Sub ClearFields()
ta1.Text = ""
labRecID.Text = ""
labTime.Text = ""
labLocation.Text = ""
End
Public Sub MenuNewNote_Click()
ClearFields
ta1.SetFocus
End
Public Sub ta1_GotFocus()
OriginalText = ta1.Text
End
Public Sub ta1_LostFocus()
If ta1.Text = OriginalText Then Return 'no change
SaveOrUpdate
End
Public Sub tbSearch_Change()
mdb.SearchFor(tbSearch.Text)
ShowRecord
Catch
Message.Error(Error.Text)
End
Public Sub ta1_KeyPress()
If Key.Code = Key.Esc Then Me.SetFocus 'clear focus from textarea; this triggers a record update
End
Public Sub tbSearch_KeyPress()
If Key.Code = Key.Esc Then Me.SetFocus
End
Public Sub MenuShowAll_Click()
mdb.SelectAllNotes
ShowRecord
End
Public Sub KeepReplacing(InThis As String, LookFor As String, Becomes As String) As String
Dim z As String = InThis
While InStr(z, LookFor) > 0
z = Replace(z, LookFor, Becomes)
Wend
Return z
End
Public Sub SaveOrUpdate()
If IsNull(labRecID.Text) Then 'new record
If IsNull(ta1.Text) Then Return
Dim d As Date = Now()
labRecID.Text = mdb.AddRecord(ta1.Text, d)
labTime.text = Format(d, gb.MediumDate) & " " & Format(d, gb.LongTime)
Else 'update
If IsNull(ta1.Text) Then
mdb.DeleteRecord(Val(labRecID.Text))
ClearFields 'maybe leave everything empty?
Else
mdb.UpdateRecord(Val(labRecID.Text), ta1.Text)
Endif
Endif
End
Public Sub MenuTidy_Click()
OriginalText = ta1.Text
If IsNull(ta1.Text) Then Return
Dim z As String = If(ta1.Selection.Length = 0, Trim(ta1.Text), Trim(ta1.Selection.Text))
z = KeepReplacing(z, gb.NewLine, "|")
z = KeepReplacing(z, gb.Tab & gb.Tab, gb.Tab)
z = KeepReplacing(z, " ", " ")
z = KeepReplacing(z, "| ", "|")
z = KeepReplacing(z, "|" & gb.tab, "|")
z = KeepReplacing(z, "||", "|")
z = KeepReplacing(z, "|", gb.NewLine)
If ta1.Selection.Length = 0 Then ta1.Text = z Else ta1.Selection.Text = z
SaveOrUpdate
End
Public Sub MenuSentences_Click()
OriginalText = ta1.Text
If IsNull(ta1.Text) Then Return
Dim z As String = If(ta1.Selection.Length = 0, Trim(ta1.Text), Trim(ta1.Selection.Text))
z = KeepReplacing(z, gb.NewLine, "~")
z = KeepReplacing(z, "~ ", "~")
z = KeepReplacing(z, ".~", "|")
z = KeepReplacing(z, "~", " ")
z = KeepReplacing(z, " ", " ")
z = KeepReplacing(z, "|", "." & gb.NewLine)
If ta1.Selection.Length = 0 Then ta1.Text = z Else ta1.Selection.Text = z
SaveOrUpdate
End
Public Sub MenuUndo_Click()
Dim z As String = ta1.Text
ta1.Text = OriginalText
OriginalText = z
SaveOrUpdate
End
Public Sub MenuRenumber_Click()
mdb.Renumber
ShowRecord
End
Public Sub MenuVacuum_Click()
Me.Caption = "File size -> " & mdb.Vacuum()
End
Public Sub MenuBackup_Click()
Me.Caption = mdb.Backup()
End
Public Sub MenuTypeExtra_Click()
ta1.SetFocus
ta1.Text &= gb.NewLine & gb.NewLine
ta1.Select(ta1.Text.Len)
End
Public Sub MenuDoubleSpace_Click()
OriginalText = ta1.Text
If IsNull(ta1.Text) Then Return
Dim z As String = If(ta1.Selection.Length = 0, Trim(ta1.Text), Trim(ta1.Selection.Text))
z = KeepReplacing(z, gb.NewLine, "|")
z = KeepReplacing(z, "||", "|")
z = KeepReplacing(z, "|", gb.NewLine & gb.NewLine)
If ta1.Selection.Length = 0 Then ta1.Text = z Else ta1.Selection.Text = z
SaveOrUpdate
End
Public Sub ti1_MiddleClick()
Me.Show
Me.Activate
tbSearch.Text = ""
mdb.SelectAllNotes
ShowRecord
End
Public Sub MenuHide_Click()
Me.Hide
End
Massage(string) 函数对于处理包含单引号的文本的保存是必要的。SQL 语句使用单引号来包围字符串。字符串中的单引号将终止字符串,后面的内容将是语法错误,因为它将是不可理解的。要包含单引号,它必须加倍。例如,要保存字符串 Fred’s house,它必须首先被转换为(“按摩”为)Fred’’s house。
KeepReplacing(InThis, LookFor, ReplaceWithThis) 函数执行替换,直到 LookFor 字符串不再存在。例如,如果您想从 abcxxxxdef 中删除多个 x,只保留一个 x,您不能只使用 Replace(“abcxxxxdef”, “xx”, “x”),因为这将产生 abcxxdef。第一个双 x 变成一个 x,第二个双 x 变成一个 x。您仍然有两个 x。您必须不断替换,直到不再存在双 x。
好了,各位,除了参考附录。我可以从开始的地方结束,向 Benoît Minisini 表示感谢。这个编程环境非常令人愉快。我们所有用户都怀着感激之情,举杯高歌,“因为他是一位快乐的绅士,我们大家都这么说”。
Gerard Buzolic
2019 年 9 月 25 日