简体   繁体   English

命令行 Swift 脚本的最小可行 GUI 是什么?

[英]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.

相关问题 Swift共享库是否为命令行应用程序静态链接? - Swift shared libraries getting statically linked for command-line apps? 在Swift 3内部命令行程序中如何执行“说”? - How to do a “say” in Swift 3 inside command-line program? 使用 Swift 使用 PythonKit 调用带有参数的 python 命令行工具 - Calling a python Command-line tool with arguments, with PythonKit using Swift 构建具有关联Swift库的Swift命令行工具时,为什么要嵌入框架? - When building a Swift command-line tool with associated Swift library why is the Framework being embedded? 命令行 Swift 测试出现奇怪的链接错误(可能是 Swift Package 管理器相关) - Strange Link Error With Command-Line Swift Test (May Be Swift Package Manager-Related) 对于Swift中的Dictionary for Search应用程序,可行的替代方法是什么? - What is a viable alternative for a Dictionary for Search applications in Swift? 从OS X命令行编译和链接Swift加上Objective C代码 - Compiling and linking Swift plus Objective C code from the OS X command-line 是否可以在命令行模式下运行仅Swift的Cocoa应用程序 - Is it possible to run a Swift-only Cocoa application in command-line mode 无法在 macOS 11.6 (Big Sur) 上的 swift gui 应用程序中发送击键 - “命令行工具”有效 - Cannot send a keystroke in swift gui application on macOS 11.6 (Big Sur) - "Command Line Tool" works xcodebuild、xcrun 和 swift 命令行工具之间有什么区别? - What are the differences between xcodebuild, xcrun and swift command line tools?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM