[英]What is the minimally-viable GUI for command-line Swift scripts?
I need a little helper GUI for my Swift script on macOS.在 macOS 上,我的 Swift 脚本需要一个小助手 GUI。 It just needs a text entry field and an OK button.
它只需要一个文本输入字段和一个确定按钮。
I don't want to go the whole bloated Xcode route just for this little popup.我不想 go 整个臃肿的 Xcode 路线只是为了这个小弹出窗口。 However, Apple's documentation fails me because keyboard input isn't captured by my NSWindow.
但是,Apple 的文档让我失望了,因为我的 NSWindow 没有捕获键盘输入。 Help!
帮助!
No thanks to Apple's documentation , I finally figured out the magical incantations ♂️ needed to launch a simple AppKit/Cocoa GUI from a command-line Swift app that accepts keyboard input .不,感谢Apple 的文档,我终于找到了从接受键盘输入的命令行 Swift 应用程序启动简单 AppKit/Cocoa GUI 所需的神奇咒语♂️。 Without Xcode !
没有 Xcode !
This is also needed to accept text input in WKWebViews.这也是在 WKWebViews 中接受文本输入所必需的。
// main.swift // Dylan Sharhon // Tested on Catalina, Nov 2019
import AppKit // import Cocoa if you also need Foundation functionality
let app = NSApplication.shared
app.setActivationPolicy(.regular) // Magic to accept keyboard input and be docked!
let window = NSWindow.init(
contentRect: NSRect(x: 300, y: 300, width: 200, height: 85),
styleMask: [
NSWindow.StyleMask.titled // Magic needed to accept keyboard input
],
backing: NSWindow.BackingStoreType.buffered,
defer: false
)
window.makeKeyAndOrderFront(nil) // Magic needed to display the window
// Text input field
let text = NSTextField.init(string: "")
text.frame = NSRect(x: 10, y: 45, width: 180, height: 25)
window.contentView!.addSubview(text)
// Button
class Target {
@objc func onClick () { // Magic @objc needed for the button action
print(text.stringValue) // Goes to stdout
exit(0)
}
}
let target = Target()
let button = NSButton.init(
title: "OK",
target: target,
action: #selector(Target.onClick)
)
button.frame = NSRect(x:50, y:10, width:100, height:30)
window.contentView!.addSubview(button)
app.run()
To noobs like me: This is the entire app and you can run it with swift main.swift
or compile it with swiftc main.swift
and rename the resulting (merely 40 KB) executable to whatever you want to be in the menubar. To noobs like me: This is the entire app and you can run it with
swift main.swift
or compile it with swiftc main.swift
and rename the resulting (merely 40 KB) executable to whatever you want to be in the menubar.
I wrote a little script which is able to show a window in swift.我写了一个小脚本,它能够在 swift 中显示 window。 The goal was to show the output of several shell commands (
brew update
& brew upgrade
& brew cleanup
& brew doctor
).目标是显示几个 shell 命令(
brew update
& brew upgrade
& brew cleanup
& brew doctor
)的 output 。 These commands can take significant amount of time if you do not perform them everyday and I was tired of having to wait sometime 10 minutes just for the 2 first commands to complete.如果您不是每天都执行这些命令,那么这些命令可能会花费大量时间,而且我已经厌倦了必须等待 10 分钟才能完成前 2 个命令。
I could have simply launched a cron job or used launchd with a shell script, but I wanted to be able to check the success or failure of the commands, especially the brew doctor
, to know if I needed to perform some action to clean my homebrew install on my machine.我本可以简单地启动一个 cron 作业或将 launchd 与 shell 脚本一起使用,但我希望能够检查命令的成功或失败,尤其是
brew doctor
,以了解我是否需要执行一些操作来清理我的自制软件安装在我的机器上。
So I needed a window to show up with the error and standard output of the commands and more, I wanted to produce a binary of it.所以我需要一个 window 来显示错误和命令的标准 output 等等,我想生成它的二进制文件。
After searching a bit on Google and Github, I found swift-sh, which allow to import Github repository (in a standardized way through Swift Package Manager) and use it in a swift script and compile it if needed; After searching a bit on Google and Github, I found swift-sh, which allow to import Github repository (in a standardized way through Swift Package Manager) and use it in a swift script and compile it if needed; and ShellOut, by the same guy, which allows to perform shell commands from a swift script and collects the output of the command in an swift object.
and ShellOut, by the same guy, which allows to perform shell commands from a swift script and collects the output of the command in an swift object.
Basically, it should have been a little window with a textview in a scrollview, which showed the output of the shell commands while being able to scroll it. Basically, it should have been a little window with a textview in a scrollview, which showed the output of the shell commands while being able to scroll it.
Here the Script:这里的脚本:
#!/usr/bin/swift sh
import AppKit
import Foundation
// importing ShellOut from GitHub repository
// The magic of swift-sh happens
import ShellOut // @JohnSundell
// Declare the Application context
let app = NSApplication.shared
// Create the delegate class responsible for the window and crontrol creation
class AppDelegate: NSObject, NSApplicationDelegate {
var str: String? = ""
// Construct the window
let theWindow = NSWindow(contentRect: NSMakeRect(200, 200, 400, 200),
styleMask: [.titled, .closable, .miniaturizable, .resizable],
backing: .buffered,
defer: false,
screen: nil)
var output: String? = ""
// What happens once application context launched
func applicationDidFinishLaunching(_ notification: Notification) {
var str = ""
// The shell commands and the collect of output
do {
str = try shellOut(to: "brew", arguments: ["update"] )
output = output! + str
} catch {
let error1 = error as! ShellOutError
//print(error1.message)
output = output! + error1.message
}
do {
str = try shellOut(to: "brew", arguments: ["upgrade"] )
output = output! + "\n" + str
//print("step 2")
} catch {
let error2 = error as! ShellOutError
//print(error2.message)
output = output! + "\n" + error2.message
}
do {
str = try shellOut(to: "brew", arguments: ["cleanup"] )
output = output! + "\n" + str
//print("step 3")
} catch {
let error3 = error as! ShellOutError
//print(error3.message)
output = output! + "\n" + error3.message
}
do {
str = try shellOut(to: "brew", arguments: ["doctor"] )
output = output! + "\n" + str
//print("step 4")
} catch {
let error4 = error as! ShellOutError
//print(error4.message)
output = output! + "\n" + error4.message
}
// Controls placement and content goes here
// ScrollView...
var theScrollview = NSScrollView(frame: theWindow.contentView!.bounds)
var contentSize = theScrollview.contentSize
theScrollview.borderType = .noBorder
theScrollview.hasVerticalScroller = true
theScrollview.hasHorizontalScroller = false
theScrollview.autoresizingMask = NSView.AutoresizingMask(rawValue: NSView.AutoresizingMask.width.rawValue | NSView.AutoresizingMask.height.rawValue | NSView.AutoresizingMask.minYMargin.rawValue | NSView.AutoresizingMask.minYMargin.rawValue)
// TextView...
var theTextView = NSTextView(frame: NSMakeRect(0, 0, contentSize.width, contentSize.height))
theTextView.minSize = NSMakeSize(0.0, contentSize.height)
theTextView.maxSize = NSMakeSize(CGFloat.greatestFiniteMagnitude, CGFloat.greatestFiniteMagnitude)
theTextView.isVerticallyResizable = true
theTextView.isHorizontallyResizable = false
theTextView.autoresizingMask = NSView.AutoresizingMask(rawValue: NSView.AutoresizingMask.width.rawValue | NSView.AutoresizingMask.height.rawValue | NSView.AutoresizingMask.minYMargin.rawValue | NSView.AutoresizingMask.minYMargin.rawValue)
theTextView.textContainer?.containerSize = NSMakeSize(contentSize.width, CGFloat.greatestFiniteMagnitude)
theTextView.backgroundColor = .white
theTextView.textContainer?.widthTracksTextView = true
theTextView.textStorage?.append(NSAttributedString(string: output!))
theScrollview.documentView = theTextView
theWindow.contentView = theScrollview
theWindow.makeKeyAndOrderFront(nil)
theWindow.makeFirstResponder(theTextView)
}
// What happens when we click the close button of the window
func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
return true;
}
}
// Instantiation of the application delegate class
// and launching
let delegate = AppDelegate()
app.delegate = delegate
app.run()
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.