[Real World Haskell翻译]第23章 GUI编程使用gtk2hs
第23章 GUI编程使用gtk2hs 在本书中,我们一直在开发简单的基于文本的工具。虽然这些往往是理想的接口,但有时图形用户界面(GUI)是必需的。有几个Haskell的GUI工具包是可用的。在本章中,我们将着眼于其中一个,gtk2hs。 %多个替代选择存在。除了gtk2hs,wxHaskell也是一个著名的跨平台GUI工具包。 安装gtk2hs 在我们和gtk2hs工作之前,你需要安装它。在大多数Linux,BSD,或其他POSIX平台,你会发现已经编译好的gtk2hs包。一般你会需要安装GTK+开发环境,Glade,和gtk2hs。具体的做法在不同发行版下有所不同。
Windows和Mac开发人员应查阅gtk2hs的下载站点位于 http://www.haskell.org/gtk2hs/download/。从下载gtk2hs开始。然后,你会还需要Glade第3版。 Mac开发者可以在http://www.macports.org/找到这个,Windows开发人员应该查阅http://sourceforge.net/projects/gladewin32。 GTK+体系结构概述 在研究代码之前,让我们暂停一会并考虑我们将要使用的系统的体系结构。首先,我们有GTK+。GTK+是一个跨平台的采用C语言编写的GUI构建工具包。它可以运行在Windows,Mac,Linux,BSD等系统。这也是GNOME桌面环境下的工具包。
接下来,我们有Glade。Glade是用户界面设计器,它可以让你以图形化方式布局出您的应用程序的窗口和对话框。Glade在XML文件中保存接口,您的应用程序在运行时会加载该文件。
体系结构中的最后一块是gtk2hs。这是GTK+,Glade和几个相关库的Haskell绑定。它是GTK+的多种语言绑定中的一个。 用Glade进行用户界面设计 在本章中,我们将为我们在第22章开发的播客下载器开发一个GUI。我们的首要任务是在Glade中设计用户界面。一旦我们完成这一点,我们将编写Haskell代码将其与应用程序集成。
因为这是一本Haskell的书,而不是一个GUI设计的书,我们将快速介绍这些早期部分中的一些。欲了解用Glade进行界面设计的更多信息,您可以参照这些资源:
Glade主页
包含Glade文档;查看http://glade.gnome.org/。
GTK+主页
包含不同widgets的信息。请参阅文档中GTK的部分;查看http://www.gtk.org/。
gtk2hs主页
也有一个有用的文档,其中包含gtk2hs的API参考;查看http://www.haskell.org/gtk2hs/documentation/。 Glade概念 Glade是一个用户界面设计工具。它让我们使用一个图形界面来设计我们的图形界面。我们可以使用一堆GTK+函数调用来建立窗口组件,但使用Glade通常更容易做这些。
我们使用GTK+工作的根本的“东西”是widget。一个widget代表图形用户界面的任何一部分,并可能含有其它widget。一些widget的例子包括window, dialog box, button, and text within the button。
Glade,是一个widget布局工具。我们建立了widget的整棵树,顶级的窗口在树的顶部。你可以认为Glade和widget在某些地方和HTML相同:你可以安排widget在一个类似表的布局,设置填充规则并分层构建整个描述。
Glade保存widget描述到一个XML文件中。我们的程序在运行时加载这个XML文件。我们通过要求Glade运行时库加载一个特定名称的widget来加载widget。
图23-1给出了用Glade来设计我们的应用程序的主屏幕的例子的截图。
在这本书的可下载的资料中,你可以找到完整的Glade XML文件作为podresources.glade。您可以在Glade中加载此文件并编辑它,如果你愿意。 %图23-1%
%图23-1。Glade截图,显示图形用户界面的组件 事件驱动编程 像许多GUI工具包,GTK+是一个事件驱动工具包。这意味着,不是显示一个对话框,并等待用户点击一个按钮,而是如果某个确定的按钮被点击,我们告诉gtk2hs调用什么函数,而不是等待对话框中的点击。
这是与控制台程序使用的传统模型不同的。当你思考它,它确实应该是这样的。一个GUI程序可能有多个窗口打开,编写代码等待打开的窗口的特定组合的输入是一个复杂的命题。
事件驱动编程是Haskell的很好的补充。正如我们已经一遍一遍在这本书中讨论过,函数式语言以传递函数为长。因此,我们将传递函数给gtk2hs,当某些事件发生时它被调用。这些被称为回调函数。
GTK+程序的核心是main循环。这是等待来自用户的action或者来自程序的命令并执行的程序的部分。
GTK+主循环完全由GTK+处理。对我们来说,它看起来像一个我们执行的I/O action,它不返回直到GUI停止。
由于主循环是负责处理从鼠标点击到重绘窗口的所有事情,它必须始终可用。我们不能只运行一个长时间运行的任务,如下载播客清单在主循环内。这将使GUI响应缓慢,点击“取消”按钮将不能及时地处理。
因此,我们将使用多线程来处理这些长时间运行的任务。更多多线程的信息,可以查阅第24章。现在,只需要知道我们将使用forkIO来创建新的线程处理长时间运行的任务,如下载播客feed和清单。对于非常快的任务,如添加一个新的播客到数据库,我们不会用到一个单独的线程因为它执行很快用户从不会注意到。 初始化GUI 我们的第一个步骤是为我们的程序初始化GUI。由于我们稍后将在本章的第528页“Using Cabal”中解释,我们将有一个叫做PodLocalMain.hs的小文件,它加载PodMain并传递给它podresources.glade的路径,这是Glade保存的XML文件,它给出了关于widget的信息: -- file: ch23/PodLocalMain.hs
module Main where import qualified PodMainGUI main = PodMainGUI.main "podresources.glade" 现在,让我们考虑PodMainGUI.hs。这个文件是唯一的Haskell源文件,我们不得不修改第22章中的例子使其作为GUI工作。让我们从研究我们的新的PodMainGUI.hs的开头开始,对了清楚,我们将PodMain.hs重新命名: -- file: ch23/PodMainGUI.hs
module PodMainGUI where import PodDownload
import PodDB
import PodTypes
import System.Environment
import Database.HDBC
import Network.Socket(withSocketsDo) -- GUI libraries import Graphics.UI.Gtk hiding (disconnect)
import Graphics.UI.Gtk.Glade -- Threading import Control.Concurrent PodMainGUI.hs的第一部分类似于我们的非GUI版本。但我们导入了三个额外的组件。首先,我们有Graphics.UI.Gtk,它提供了我们将使用的大多数的GTK+的函数。这个模块和Database.HDBC都提供一个名为disconnect的函数。由于我们将使用HDBC版本,而不使用GTK+版本,我们不从Graphics.UI.Gtk导入该函数。Graphics.UI.Gtk.Glade包含加载和处理Glade文件所需的函数。
我们还导入了Control.Concurrent,其中有多线程编程所需的基础。一旦我们进入程序的内部,我们将使用来自上述的一些函数。接下来,让我们来定义一个存储我们的GUI的信息的类型: -- file: ch23/PodMainGUI.hs
-- | Our main GUI type
data GUI = GUI {
mainWin :: Window,
mwAddBt :: Button,
mwUpdateBt :: Button,
mwDownloadBt :: Button,
mwFetchBt :: Button,
mwExitBt :: Button,
statusWin :: Dialog,
swOKBt :: Button,
swCancelBt :: Button,
swLabel :: Label,
addWin :: Dialog,
awOKBt :: Button,
awCancelBt :: Button,
awEntry :: Entry} 我们的新type存储我们在整个程序中关心的所有的widget。大的程序可能不希望有这样的统一的类型。对于这个小例子,它是有意义的,因为它可以很容易地传递给不同的函数,我们将知道,我们总是有我们需要的信息在可用状态。
在此记录中,我们有Window(顶层窗口),Dialog(对话窗口),Button(可点击的按钮),Label(一段文字)和Entry(用户输入文字的地方)字段。现在,让我们来看看我们的main函数: -- file: ch23/PodMainGUI.hs
main :: FilePath -> IO ()
main gladepath = withSocketsDo $ handleSqlError $
do initGUI -- Initialize GTK+ engine -- Every so often, we try to run other threads.
timeoutAddFull (yield >> return True)
priorityDefaultIdle 100 -- Load the GUI from the Glade file
gui <- loadGlade gladepath -- Connect to the database
dbh <- connect "pod.db" -- Set up our events
connectGui gui dbh -- Run the GTK+ main loop; exits after GUI is done
mainGUI -- Disconnect from the database at the end
disconnect dbh 请记住这个main函数的类型和平常有点不同,因为它被PodLocalMain.hs中的main所调用。我们通过调用initGUI开始,它初始化GTK+系统。接下来,我们调用timeoutAddFull。这个调用只为多线程GTK+程序需要。它每隔一段时间告诉GTK+ main循环去暂停给其他线程运行的机会。
在那之后,我们调用loadGlade函数(见下面的代码)来从我们的Glade XML文件加载widget。下一步,我们连接我们的数据库,并调用我们的connectGui函数来设置我们的回调函数。然后,我们点燃GTK+ main循环。我们预计它可能是数分钟,数小时,甚至数天在mainGUI返回前。当它返回时,这意味着用户已经关闭了主窗口,或点击了“退出”按钮。在那之后,我们从数据库断开连接,并关闭该程序。现在,让我们看看我们的loadGlade函数: -- file: ch23/PodMainGUI.hs
loadGlade gladepath =
do -- Load XML from glade path.
-- Note: crashes with a runtime error on console if fails!
Just xml <- xmlNew gladepath -- Load main window
mw <- xmlGetWidget xml castToWindow "mainWindow" -- Load all buttons [mwAdd, mwUpdate, mwDownload, mwFetch, mwExit, swOK, swCancel,
auOK, auCancel] <-
mapM (xmlGetWidget xml castToButton)
["addButton", "updateButton", "downloadButton",
"fetchButton", "exitButton", "okButton", "cancelButton",
"auOK", "auCancel"] sw <- xmlGetWidget xml castToDialog "statusDialog"
swl <- xmlGetWidget xml castToLabel "statusLabel" au <- xmlGetWidget xml castToDialog "addDialog"
aue <- xmlGetWidget xml castToEntry "auEntry" return $ GUI mw mwAdd mwUpdate mwDownload mwFetch mwExit
sw swOK swCancel swl au auOK auCancel aue 此函数通过调用xmlNew开始,它载入Glade XML文件。它在错误时返回Nothing。成功时我们使用模式匹配来提取结果值。如果失败的话,会有一个控制台(非图形界面)异常显示出来;在本章结尾的练习会阐述这个。
现在,我们有已加载的Glade的XML文件,你会看到一系列对xmlGetWidget的调用。此Glade函数是用来加载定义了widget的XML并为那个widget返回一个GTK+ widget类型。我们要传递给那个函数一个表明我们期待的GTK+类型的值,我们将得到一个运行时错误,如果这些不匹配。
我们从创建一个主窗口widget开始。它在XML widget中名为“mainWindow”,我们加载它并把它存储在mw变量中。然后,我们使用模式匹配和mapM来加载所有的button。然后,我们有两个dialog,一个label和一个entry被载入。最后,我们使用所有这些来建立GUI类型并返回它。接下来,我们需要建立我们的回调函数作为事件处理程序: -- file: ch23/PodMainGUI.hs
connectGui gui dbh =
do -- When the close button is clicked, terminate the GUI loop
-- by calling GTK mainQuit function
onDestroy (mainWin gui) mainQuit -- Main window buttons
onClicked (mwAddBt gui) (guiAdd gui dbh)
onClicked (mwUpdateBt gui) (guiUpdate gui dbh)
onClicked (mwDownloadBt gui) (guiDownload gui dbh)
onClicked (mwFetchBt gui) (guiFetch gui dbh)
onClicked (mwExitBt gui) mainQuit -- We leave the status window buttons for later 我们通过调用OnDestroy启动connectGui函数。这意味着,当有人点击操作系统的关闭按钮(Windows或Linux上通常是一个在标题栏的X或Mac OS X上的一个红色圆圈),我们在主窗口上调用mainQuit函数。mainQuit关闭了所有的GUI窗口并终止了GTK+的main循环。
接下来,我们调用onClicked为点击五个不同的按钮来注册事件处理程序。对于按钮,如果用户通过键盘选择按钮,这些handler也会被调用。点击这些按钮将调用我们的函数,如guiAdd,传递GUI记录以及数据库handle。
在这一点上,我们已经完全为图形用户界面的播客采集软体的定义了主窗口。它看起来像图23-2中的截图。 %图23-2%
%图23-2。播客采集应用程序的主窗口截图 添加播客窗口 现在,我们已经介绍了主窗口,让我们来谈谈我们的应用呈现的其他的窗口,从添加播客的窗口开始。当用户点击按钮来添加一个新的播客,我们需要弹出一个对话框并提示输入播客的URL。我们在Glade中定义了这个对话框,所以我们需要做的就是将它加载进来: -- file: ch23/PodMainGUI.hs
guiAdd gui dbh =
do -- Initialize the add URL window
entrySetText (awEntry gui) ""
onClicked (awCancelBt gui) (widgetHide (addWin gui))
onClicked (awOKBt gui) procOK -- Show the add URL window
windowPresent (addWin gui)
where procOK =
do url <- entryGetText (awEntry gui)
widgetHide (addWin gui) -- Remove the dialog
add dbh url -- Add to the DB 我们通过调用设置输入框内容为空字符串(用户输入URL的地方)的entrySetText开始。因为相同的widget在程序的整个生命周期得到重用,我们不希望用户输入的URL留在那里。接下来,我们为对话框中的两个按钮设置action。如果用户点击“取消”按钮,我们只需通过调用widgetHideon删除屏幕上的对话框。如果用户点击“确定”按钮,我们调用procOK。
procOK通过检索由入口的widget所提供的URL开始。接着,它使用widgetHide来脱离对话框。最后,它调用add把URL添加到数据库。此add和我们在非图形化界面版本的程序中的函数是完全相同。
我们在guiAdd中做的最后一件事实际上是显示弹出式窗口。这是通过调用和widgetHide相对的windowPresent完成的。 %图23-3%
%图23-3。 增加播客窗口截图
%需要注意的是guiAdd函数几乎立即返回。它只是设置并启动widget,显示这些widget;它在任何时候阻塞并等待输入。图23-3展示了对话框的样子。 长时间运行的任务 我们认为在主窗口中的按钮是可用的,他们三个对应需要一段时间完成的任务:更新,下载,和获取。当这些操作进行的时候,我们希望在我们的GUI中做两件事情:为用户提供操作的状态和在它运行时取消操作的能力。
由于这三件事情是非常相似的操作,提供通用的方式处理这些交互是在情理之中。我们已经在Glade文件中定义了一个单独的状态窗口widget,它将被这些交互使用。在我们的Haskell源代码中,我们将定义一个用于这三个操作的通用的statusWindow函数。
statusWindow需要四个参数:GUI信息,数据库信息,一个窗口标题的String,一个执行任务的函数。此函数将被传递给一个可以报告它的进度的函数。代码如下: -- file: ch23/PodMainGUI.hs
statusWindow :: IConnection conn =>
GUI
-> conn
-> String
-> ((String -> IO ()) -> IO ())
-> IO ()
statusWindow gui dbh title func =
do -- Clear the status text
labelSetText (swLabel gui) "" -- Disable the OK button, enable Cancel button
widgetSetSensitivity (swOKBt gui) False
widgetSetSensitivity (swCancelBt gui) True -- Set the title
windowSetTitle (statusWin gui) title -- Start the operation
childThread <- forkIO childTasks -- Define what happens when clicking on Cancel
onClicked (swCancelBt gui) (cancelChild childThread) -- Show the window
windowPresent (statusWin gui)
where childTasks =
do updateLabel "Starting thread..."
func updateLabel
-- After the child task finishes, enable OK
-- and disable Cancel
enableOK enableOK =
do widgetSetSensitivity (swCancelBt gui) False
widgetSetSensitivity (swOKBt gui) True
onClicked (swOKBt gui) (widgetHide (statusWin gui))
return () updateLabel text =
labelSetText (swLabel gui) text
cancelChild childThread =
do killThread childThread
yield
updateLabel "Action has been cancelled."
enableOK 此函数从清除来自最后运行的标签文本开始。接下来,我们禁用(设成灰色)“确定”按钮并启用“取消”按钮。当操作在进行中时,单击“确定”并没有太大的意义。当它完成时,单击“取消”也不会有多大的意义。
接下来,我们设置窗口的标题。标题是系统在窗口的标题栏中显示的一部分。最后,我们开始新的线程(显示为childTasks)并保存其线程ID。然后,我们定义了如果用户点击取消我们将调用cancelChild,并传递线程ID给它。最后,我们调用windowPresent显示状态窗口。
在childTasks中,我们显示一个消息,“我们正在启动线程”。然后我们调用实际工作的函数,传递给用于显示状态消息的updateLabelas函数。需要注意的是命令行版本的程序可以在这里传递putStrLn。
最后,worker函数退出后,我们调用enableOK。该函数禁用“取消”按钮,启用“确定”按钮,并定义点击“确定”按钮将导致状态窗口消失。
updateLabel在label widget上简单地调用labelSetText更新显示的文本。最后,cancelChild杀掉正在处理任务的线程,更新label,并启用“确定”按钮。
我们现在有基础来定义我们的三个GUI函数。他们看起来像这样: -- file: ch23/PodMainGUI.hs
guiUpdate :: IConnection conn => GUI -> conn -> IO ()
guiUpdate gui dbh =
statusWindow gui dbh "Pod: Update" (update dbh) guiDownload gui dbh =
statusWindow gui dbh "Pod: Download" (download dbh) guiFetch gui dbh =
statusWindow gui dbh "Pod: Fetch"
(\logf -> update dbh logf >> download dbh logf) 为简单起见,我们只给出了第一个的类型,但这三个具有相同的类型,Haskell可以通过类型推断推断出来。请注意我们的guiFetch的实现。我们不能调用statusWindow两次,但是可以结合在action中组合函数。
代码的最后一块由三个函数组成。add来自命令行的章节且未被修改。update和download仅修改去取得一个logging函数,而不是调用putStrLn用于状态更新。 -- file: ch23/PodMainGUI.hs
add dbh url =
do addPodcast dbh pc
commit dbh
where pc = Podcast {castId = 0, castURL = url} update :: IConnection conn => conn -> (String -> IO ()) -> IO ()
update dbh logf =
do pclist <- getPodcasts dbh
mapM_ procPodcast pclist
logf "Update complete."
where procPodcast pc =
do logf $ "Updating from " ++ (castURL pc)
updatePodcastFromFeed dbh pc download dbh logf =
do pclist <- getPodcasts dbh
mapM_ procPodcast pclist
logf "Download complete."
where procPodcast pc =
do logf $ "Considering " ++ (castURL pc)
episodelist <- getPodcastEpisodes dbh pc
let dleps = filter (\ep -> epDone ep == False)
episodelist
mapM_ procEpisode dleps
procEpisode ep =
do logf $ "Downloading " ++ (epURL ep)
getEpisode dbh ep 图23-4显示了运行更新后最后的结果看起来是什么样。 %图23-4%
%图23-4。显示“更新完成”的对话框的截图 使用Cabal 我们在“第515页展示了一个建立这个项目的命令行版本的Cabal文件。我们需要做出一些调整使它工作在我们的GUI版本下。首先,明显需要添加gtk2hs包到构建依赖关系的列表。Glade XML文件也需要用这个方法处理。
此前,我们写了一个PodLocalMain.hs文件,简单地假设该文件被命名为podresources.glade并存储在当前工作目录。对于一个真正的,系统级的安装,我们不能做这样的假设。此外,不同的系统可能把文件放置在不同的位置。
Cabal提供了解决这个问题的一种方法。它会自动生成一个根据环境的不同导出函数的模块。我们必须添加一个Data-files行到我们的Cabal描述文件。所有数据文件的文件名将会是系统级安装的一部分。接着,Cabal将导出在运行时我们可以询问位置的Paths_pod模块(“pod”部分来自Cabal文件中的Name行)。这里是我们的新的Cabal描述文件: -- ch24/pod.cabal
Name: pod
Version: 1.0.0
Build-type: Simple
Build-Depends: HTTP, HaXml, network, HDBC, HDBC-sqlite3, base,
gtk, glade
Data-files: podresources.glade Executable: pod
Main-Is: PodCabalMain.hs
GHC-Options: -O2 接着这里是PodCabalMain.hs: -- file: ch23/PodCabalMain.hs
module Main where import qualified PodMainGUI
import Paths_pod(getDataFileName) main =
do gladefn <- getDataFileName "podresources.glade"
PodMainGUI.main gladefn %练习
%1。如果调用xmlNew返回Nothing,就显示一个有帮助的GUI错误消息。
%2。修改播客采集软体使其可以运行在GUI或命令行界面。提示:将共同的命令从PodMainGUI.hs移出,然后有两个不同的Main模块,一个用于GUI,和一个用于
命令行。
%3。为什么guiFetch连接worker函数,而不是调用statusWindow两次?
[Real World Haskell翻译]第23章 GUI编程使用gtk2hs的更多相关文章
- Java基础学习总结(23)——GUI编程
一.AWT介绍 所有的可以显示出来的图形元素都称为Component,Component代表了所有的可见的图形元素,Component里面有一种比较特殊的图形元素叫Container,Containe ...
- [Real World Haskell翻译]第20章 Haskell系统编程
第20章 Haskell系统编程 到目前为止,我们已经讨论了大多数的高层次的概念.Haskell也可以用于较低级别的系统编程.很可能是用haskell编写出底层的与操作系统接口的程序. 在本章中,我们 ...
- [Real World Haskell翻译]第27章 网络通信和系统日志 Sockets and Syslog
第27章 网络通信和系统日志 Sockets and Syslog 基础网络 在本书的前面几章,我们讨论了运转在网络上的服务.其中的两个例子是客户端/服务器架构的数据库和Web服务.当需要制定一个新的 ...
- [Real World Haskell翻译]第21章 使用数据库
第21章 使用数据库 从网络论坛到播客采集软件甚至备份程序的一切频繁地使用持久存储的数据库.基于SQL的数据库往往是相当方便:速度快,可扩展从微小到巨大的尺寸,可以在网络上运行,经常帮助处理锁定和事务 ...
- [Real World Haskell翻译]第24章 并发和多核编程 第一部分并发编程
第24章 并发和多核编程 第一部分并发编程 当我们写这本书的时候,CPU架构正在以比过去几十年间更快的速度变化. 并发和并行的定义 并发程序需要同时执行多个不相关任务.考虑游戏服务器的例子:它通常是由 ...
- [Real World Haskell翻译]第22章 扩展示例:Web客户端编程
第22章 扩展示例:Web客户端编程 至此,您已经看到了如何与数据库交互,解析一些数据,以及处理错误.现在让我们更进了一步,引入Web客户端库的组合. 在本章,我们将开发一个真正的应用程序:一个播客下 ...
- 《Programming WPF》翻译 第7章 1.图形基础
原文:<Programming WPF>翻译 第7章 1.图形基础 WPF使得在你的应用程序中使用图形很容易,以及更容易开发你的显卡的能力.这有很多图形构架的方面来达到这个目标.其中最重要 ...
- 《Programming WPF》翻译 第7章 2.图形
原文:<Programming WPF>翻译 第7章 2.图形 图形时绘图的基础,代表用户界面树的元素.WPF支持多种不同的形状,并为它们每一个都提供了元素类型. 7.2.1基本图形类 在 ...
- 《Programming WPF》翻译 第3章 1.什么是控件
原文:<Programming WPF>翻译 第3章 1.什么是控件 对于一个应用程序而言,控件是搭建用户界面的积木.它们具备交互式的特征,例如文本框.按钮以及列表框.尽管如此,WPF还有 ...
随机推荐
- yii2.0发送qq邮件详情配置
首先要想使用qq发送邮件必须打开使用的qq邮箱里的一个配置,
- 如何用SAP Cloud for Customer的手机App创建销售订单
第一次启动App,设置一个初始化的pin code: 输入C4C tenant的url,用户名和密码: 登录进系统后,找到Sales Order工作中心: 可以看到系统里很多已有的销售订单了.点击屏幕 ...
- [转]java中的Static class
转自:http://www.cnblogs.com/kissazi2/p/3971065.html Java中的类可以是static吗?答案是可以.在java中我们可以有静态实例变量.静态方法.静态块 ...
- 解决Ubuntu启动错误——kernel panic not syncing vfs unable to mount root fs on unknown-block 0 0 – error
最近在倒腾Ubuntu,然后想着怎么美化一下界面,于是照着网上的教程整了一下Flatabulous这个软件,然后好像/boot就满了.关机之后再开机就出现了如题所述的错误,无法开机,也无法进入reco ...
- Ubuntu下Apache配置网站根路径
安装之后apache默认的跟路径是/var/www/ 如何修改这个默认路径呢? 直接编辑/etc/apache2/sites-available/default-ssl.conf,将Docum ...
- Linux修改权限命令chmod用法详解
Linux系统中的每个文件和目录都有访问许可权限,用它来确定谁可以通过何种方式对文件和目录进行访问和操作. 文件或目录的访问权限分为只读,只写和可执行三种.以文件为例,只读权限表示只允许读其内容,而禁 ...
- _bstr_t可接受多字节、UNICODE字符串,方便用以字符集转换
使用_bstr_t需要包含的头文件: #include <comutil.h> #include <comdef.h> // test.cpp : 定义控制台应用程序的入口点. ...
- ESlint 语法检测配置说明
部分vue-cli脚手架创建的默认eslint规则: 代码末尾不能加分号 ; 代码中不能存在多行空行 tab键不能使用,必须换成两个空格 代码中不能存在声明了但未使用的变量 关闭eslint 这里只说 ...
- C#通过指针读取文件
// readfile.cs // 编译时使用:/unsafe // 参数:readfile.txt // C#通过指针读取文件.使用该程序读并显示文本文件. using System; using ...
- 【MongoDB】CentOS上安装MongoDB
权限部分尚未测试完成,请勿参考. 1.去官方网站下载Mongodb for linux的包,我没找到CentOS的,随便下载了个mongodb-linux-x86_64-amazon-3.2.0.tg ...