diff options
| -rw-r--r-- | cmd/rescribe/gui.go | 140 | ||||
| -rw-r--r-- | cmd/rescribe/main.go | 116 | 
2 files changed, 146 insertions, 110 deletions
diff --git a/cmd/rescribe/gui.go b/cmd/rescribe/gui.go new file mode 100644 index 0000000..4944e42 --- /dev/null +++ b/cmd/rescribe/gui.go @@ -0,0 +1,140 @@ +// Copyright 2021 Nick White. +// Use of this source code is governed by the GPLv3 +// license that can be found in the LICENSE file. + +package main + +import ( +	"bufio" +	"fmt" +	"io" +	"log" +	"os" +	"path/filepath" +	"strings" +	 +	"fyne.io/fyne/v2" +	"fyne.io/fyne/v2/app" +	"fyne.io/fyne/v2/container" +	"fyne.io/fyne/v2/dialog" +	"fyne.io/fyne/v2/layout" +	"fyne.io/fyne/v2/theme" +	"fyne.io/fyne/v2/widget" +) + +// copyStdoutToChan creates a pipe to copy anything written +// to stdout instead to a rune channel +func copyStdoutToChan() (chan rune, error) { +	c := make(chan rune) + +	origStdout := os.Stdout +	r, w, err := os.Pipe() +	if err != nil { +		return c, fmt.Errorf("Error creating pipe for stdout redirection: %v", err) +	} +	os.Stdout = w + +	bufReader := bufio.NewReader(r) + +	go func() { +		defer func() { +			close(c) +			w.Close() +			os.Stdout = origStdout +		}() +		for { +			r, _, err := bufReader.ReadRune() +			if err != nil && err != io.EOF { +				return +			} +			c <- r +			if err == io.EOF { +				return +			} +		} +	}() + +	return c, nil +} + +// startGui starts the gui process +func startGui(log log.Logger, cmd string, training string, systess bool, tessdir string) error { +	myApp := app.New() +	myWindow := myApp.NewWindow("Rescribe OCR") + +	var gobtn *widget.Button + +	dir := widget.NewEntry() +	dir.SetPlaceHolder("Folder to process") +	dir.OnChanged = func(s string) { +		// TODO: also check if string is a directory, and only enable if so +		if dir.Text != "" { +			gobtn.Enable() +		} else { +			gobtn.Disable() +		} +	} + +	openbtn := widget.NewButtonWithIcon("Choose folder", theme.FolderOpenIcon(), func() { +		dialog.ShowFolderOpen(func(uri fyne.ListableURI, err error) { +			if err == nil && uri != nil { +				dir.SetText(uri.Path()) +			} +	}, myWindow)}) + +	progressBar := widget.NewProgressBar() + +	logarea := widget.NewMultiLineEntry() +	logarea.Disable() + + +	// TODO: have the button be pressed if enter is pressed +	gobtn = widget.NewButtonWithIcon("Process OCR", theme.UploadIcon(), func() { +		if dir.Text == "" { +			return +		} + +		gobtn.Disable() +		gobtn.SetText("Processing...") + +		progressBar.SetValue(0.5) + +		stdout, err := copyStdoutToChan() +		if err != nil { +			fmt.Fprintf(os.Stderr, "Error copying stdout to chan: %v\n", err) +			return +		} + +		// update log area with output from outC in a concurrent goroutine +		go func() { +			for r := range stdout { +				logarea.SetText(logarea.Text + string(r)) +				logarea.CursorRow = strings.Count(logarea.Text, "\n") +				// TODO: set text on progress bar, or a label below it, to latest line printed, rather than just using a whole multiline entry like this +				// TODO: parse the stdout and set progressBar based on that +			} +		}() + +		err = startProcess(log, cmd, dir.Text, filepath.Base(dir.Text), training, systess, dir.Text, tessdir) +		if err != nil { +			fmt.Fprintf(os.Stderr, "Error executing process: %v\n", err) +			return +		} + +		progressBar.SetValue(1.0) +		gobtn.SetText("Process OCR") +		gobtn.Enable() +	}) +	gobtn.Disable() + +	diropener := container.New(layout.NewGridLayout(2), dir, openbtn) + +	content := container.NewVBox(diropener, gobtn, progressBar, logarea) + +	myWindow.SetContent(content) + +	myWindow.Show() +	myApp.Run() + +	return nil +} diff --git a/cmd/rescribe/main.go b/cmd/rescribe/main.go index 6ff99e7..3944ace 100644 --- a/cmd/rescribe/main.go +++ b/cmd/rescribe/main.go @@ -11,7 +11,6 @@ package main  import (  	"archive/zip" -	"bufio"  	"bytes"  	_ "embed"  	"errors" @@ -28,21 +27,13 @@ import (  	"strings"  	"time" -	"fyne.io/fyne/v2" -	"fyne.io/fyne/v2/app" -	"fyne.io/fyne/v2/container" -	"fyne.io/fyne/v2/dialog" -	"fyne.io/fyne/v2/layout" -	"fyne.io/fyne/v2/theme" -	"fyne.io/fyne/v2/widget" -  	"rescribe.xyz/bookpipeline"  	"rescribe.xyz/utils/pkg/hocr"  	"rescribe.xyz/bookpipeline/internal/pipeline"  ) -const usage = `Usage: rescribe [-v] [-t training] bookdir [savedir] +const usage = `Usage: rescribe [-v] [-gui] [-systess] [-tesscmd] [-t training] bookdir [savedir]  Process and OCR a book using the Rescribe pipeline on a local machine. @@ -151,6 +142,7 @@ func main() {  	}  	verbose := flag.Bool("v", false, "verbose") +	usegui := flag.Bool("gui", false, "Use graphical user interface")  	systess := flag.Bool("systess", false, "Use the system installed Tesseract, rather than the copy embedded in rescribe.")  	training := flag.String("t", "rescribev8_fast.traineddata", `Path to the tesseract training file to use.  These training files are included in rescribe, and are always available: @@ -243,107 +235,11 @@ These training files are included in rescribe, and are always available:  		log.Fatalln("Error setting TESSDATA_PREFIX:", err)  	} -	if flag.NArg() < 1 { -		myApp := app.New() -		myWindow := myApp.NewWindow("Rescribe OCR") - -		var gobtn *widget.Button - -		dir := widget.NewEntry() -		dir.SetPlaceHolder("Folder to process") -		dir.OnChanged = func(s string) { -			// TODO: also check if string is a directory, and only enable if so -			if dir.Text != "" { -				gobtn.Enable() -			} else { -				gobtn.Disable() -			} +	if flag.NArg() < 1 || *usegui { +		err := startGui(*verboselog, tessCommand, trainingName, *systess, tessdir) +		if err != nil { +			log.Fatalln("Error in gui:", err)  		} - -		openbtn := widget.NewButtonWithIcon("Choose folder", theme.FolderOpenIcon(), func() { -			dialog.ShowFolderOpen(func(uri fyne.ListableURI, err error) { -				if err == nil && uri != nil { -					dir.SetText(uri.Path()) -				} -		}, myWindow)}) - -		progressBar := widget.NewProgressBar() - -		logarea := widget.NewMultiLineEntry() -		logarea.Disable() - - -		// TODO: have the button be pressed if enter is pressed -		gobtn = widget.NewButtonWithIcon("Process OCR", theme.UploadIcon(), func() { -			if dir.Text == "" { -				return -			} - -			gobtn.Disable() -			gobtn.SetText("Processing...") - -			progressBar.SetValue(0.5) - - -			// https://stackoverflow.com/questions/10473800/in-go-how-do-i-capture-stdout-of-a-function-into-a-string -			// https://eli.thegreenplace.net/2020/faking-stdin-and-stdout-in-go/ -			origStdout := os.Stdout -			r, w, err := os.Pipe() -			if err != nil { -				log.Fatalln("Error creating pipe for stdout redirection: ", err) -			} -			os.Stdout = w -			defer func() { -				w.Close() -				os.Stdout = origStdout -			}() - -			bufReader := bufio.NewReader(r) -			outC := make(chan rune) -			go func() { -				for { -					r, _, err := bufReader.ReadRune() -					if err != nil && err != io.EOF { -						log.Fatalf("Error reading stdout: %v", err) -						return -					} -					outC <- r -					if err == io.EOF { -						close(outC) -						return -					} -				} -			}() - -			// update log area with output from outC in a concurrent goroutine -			go func() { -				for r := range outC { -					logarea.SetText(logarea.Text + string(r)) -					logarea.CursorRow = strings.Count(logarea.Text, "\n") -					// TODO: set text on progress bar, or a label below it, to latest line printed, rather than just using a whole multiline entry like this -					// TODO: parse the stdout and set progressBar based on that -				} -			}() - -			err = startProcess(*verboselog, tessCommand, dir.Text, filepath.Base(dir.Text), trainingName, *systess, dir.Text, tessdir) -			if err != nil { -				log.Fatalln(err) -			} - -			progressBar.SetValue(1.0) -			gobtn.SetText("Process OCR") -			gobtn.Enable() -		}) -		gobtn.Disable() - -		diropener := container.New(layout.NewGridLayout(2), dir, openbtn) - -		content := container.NewVBox(diropener, gobtn, progressBar, logarea) - -		myWindow.SetContent(content) - -		myWindow.Show() -		myApp.Run()  		return  	}  | 
