前提・実現したいこと
マークシート読取りソフトを支援するプログラムをKotlinとJavaFXを用いて作成しています。
実装したい内容は
- PDFファイルをjpeg画像に変換する機能
- CSVファイルを解析し,データの加工をする機能
の2つであり,これらの実装は問題なく動いています。
その中で,進捗状況(ログ)を非同期にTextAreaに表示したいですのですが
ここで期待通りの動作をしません。
発生している問題・エラーメッセージ
コンソールには正常にログが流れており,実装内容も正常に動作しています。
しかし,TextAreaには何も表示されず,エラーログも出ていません。
該当のソースコード
######Main.kt
Kotlin
1import javafx.concurrent.Task 2import java.util.concurrent.Executors 3(略) 4 5 6fun main(args: Array<String>) { 7 Application.launch(MarkAnalyzer::class.java, *args) 8} 9 10class MarkAnalyzer : Application() { 11 12 override fun start(primaryStage: Stage) { 13 14 val root = FXMLLoader.load<Parent>(javaClass.classLoader.getResource("markanalyzer.fxml")) 15 16 primaryStage.title = "MarkAnalyzer" 17 18 val scene = Scene(root) 19 20 primaryStage.scene = scene 21 primaryStage.setOnCloseRequest { exitProcess(0) } 22 23 // アイコンを指定する 24 val iconURI = javaClass.classLoader.getResource(略) 25 if (iconURI != null) { 26 val iconImage = Image(iconURI.toString()) 27 primaryStage.icons.add(iconImage) 28 } 29 30 primaryStage.show() 31 } 32} 33 34data class Team( 35 val id: String, 36 val grade : String, 37 val classNumber : String, 38 val game : String, 39 val team : String, 40 val player : List<String> 41) 42 43class Controller : Initializable { 44 45 @FXML 46 lateinit var convertButton : Button 47 48 @FXML 49 lateinit var analyzeButton : Button 50 (略) 51 52 @FXML 53 lateinit var logTextArea : TextArea 54 55 private var csvFile = File("") 56 private var exportDirectory = File("") 57 private var pdffiles : List<File> = listOf() 58 59 companion object { 60 lateinit var instance : Controller 61 } 62 63 override fun initialize(location: URL?, resources: ResourceBundle?) { 64 instance = this 65 66 ファイル選択ダイアログに関する実装(略) 67 68 logTextArea.textProperty().addListener { _, _, _ -> 69 logTextArea.scrollTop = Double.MAX_VALUE 70 } 71 } 72 73 private val service = Executors.newSingleThreadScheduledExecutor() 74 75 @FXML 76 fun convertPdf() { 77 if (pdffiles.isEmpty()) { 78 return 79 } 80 81 val task: Task<Long> = object : Task<Long>() { 82 override fun call(): Long { 83 for (file in pdffiles) { 84 val filename = file.name 85 val pdf = PDDocument.load(file) 86 val pdfRenderer = PDFRenderer(pdf) 87 88 log("$filename を読み込みました") 89 log("$filename は${pdf.pages.count}ページあります") 90 91 for ((index, page) in pdf.pages.withIndex()) { 92 log("$filename の ${index + 1} ページ目を処理中です") 93 val bim = pdfRenderer.renderImageWithDPI(index, 300f, ImageType.BINARY) 94 95 if (removalNoiseCheckBox.isSelected) { 96 97 } 98 99 ImageIOUtil.writeImage(bim, "${exportFolderLabel.text}\$filename-${index + 1}.jpg", 300) 100 } 101 102 log("$filename の処理が完了しました") 103 pdf.close() 104 } 105 106 return 1 107 } 108 } 109 110 task.onFailed = EventHandler { event -> event.source.exception.printStackTrace() } 111 task.onSucceeded = EventHandler { println("Done.") } 112 113 logTextArea.textProperty().bind(task.messageProperty()) 114 115 service.execute(task) 116 } 117 118 @FXML 119 fun analyzeCSV() { 120 if (!csvFile.exists()) { 121 log("エラー CSVファイルの取得に失敗しました") 122 return 123 } 124 125 val task : Task<Long> = object : Task<Long>() { 126 override fun call(): Long { 127 val csvAnalyzer = CSVAnalyze(csvFile) 128 csvAnalyzer.analyze() 129 130 return 1 131 } 132 } 133 134 task.onFailed = EventHandler { event -> event.source.exception.printStackTrace() } 135 task.onSucceeded = EventHandler { println("Done.") } 136 137 logTextArea.textProperty().bind(task.messageProperty()) 138 139 service.execute(task) 140 } 141 142 private fun applyButtonStatus() { 143 convertButton.isDisable = (pdffiles.isEmpty() || !exportDirectory.exists()) 144 analyzeButton.isDisable = !csvFile.exists() 145 } 146 147 148 fun log(txt: String) { 149 // このprintlnは正常に動作 150 println("log called with $txt") 151 152 val presentDateTime = LocalDateTime.now() 153 val logtext = "[" + presentDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) + "] $txt" 154 155 // この内容が反映されない 156 logTextArea.appendText(logtext + System.getProperty("line.separator")) 157 } 158 159}
CSVAnalyzer.kt
Kotlin
1import 略 2 3class CSVAnalyze(file: File) { 4 5 private val controller = Controller.instance 6 7 private var records : List<List<String>> 8 9 private var inputCsvFile : File = file 10 11 init { 12 controller.log("CSVAnalyze インスタンスが生成されました。") 13 14 // 読み込み行のリストを作成する 15 val lines = arrayListOf<List<String>>() 16 17 controller.log("CSVファイルの読み込みを開始します") 18 // CSVファイルを読み込む(Windowsで扱うためShift_JIS) 19 inputCsvFile.forEachLine(charset("Shift_JIS")) { 20 if (it.isNotBlank()) { 21 lines.add(it.split(',')) 22 } 23 } 24 controller.log("${file.name}を読み込みました。") 25 26 // 先頭行はタイトル行なので削除する 27 // TODO: トグル? 28 lines.removeAt(0) 29 30 // 変数へ代入 31 records = lines 32 33 // デバッグ用 34 //println(lines) 35 } 36 37 fun analyze() { 38 val teamlist = mutableListOf<Team>() 39 40 // 一行ずつ回していく 41 for ((recordindex, line) in records.withIndex()) { 42 controller.log("[rowdata: $recordindex]" + line.joinToString(", ")) 43 val players = arrayListOf<String>() 44 45 val id = line[3] 46 val grade = line[4].replace("0", "") 47 val classNumber = classnumber(line[5]) 48 val game = line[6] 49 val team = line[7] 50 51 52 // 列を参照し選手の4ケタ番号を取り出している 53 for ((index, value) in line.withIndex()) { 54 if (index in 8..25) { 55 if (value.isBlank()) { 56 continue 57 } 58 val onesplace = if (line[index + 18].toInt() == 10) 9 else line[index + 18].toInt() - 1 59 val tensplace = if (value.toInt() == 10) 9 else value.toInt() - 1 60 61 if (onesplace == 0 && tensplace == 0) continue 62 63 val fourdigit = "$grade$classNumber$tensplace$onesplace" 64 players.add(fourdigit) 65 } 66 players.sort() 67 } 68 69 controller.log("[${recordindex+1}行目] ${grade}年${classNumber}組 ゲーム($game) チーム($team) プレイヤー${players}") 70 71 teamlist.add(Team(id, grade, classNumber, game, team, players)) 72 } 73 74 controller.log("CSVファイルの解析が終了しました。") 75 76 val sortedList = teamlist.sortedWith(compareBy({it.game}, {it.grade}, {it.classNumber}, {it.team})) 77 78 controller.log("項目を昇順に並び替えました。") 79 controller.log("出力ファイルの生成を開始します。") 80 81 val nowtime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("MMdd-hhmmss")) 82 val writeCsvFile = File(inputCsvFile.parent + "/加工後_$nowtime.csv") 83 val osw = OutputStreamWriter(FileOutputStream(writeCsvFile), "Shift_JIS") 84 val bw = BufferedWriter(osw) 85 86 bw.write("連番,学年,クラス,競技,チーム,選手") 87 bw.newLine() 88 var i = 1 89 for (team in sortedList) { 90 val sb = StringBuilder() 91 92 sb.append(i).append(",").append(team.grade).append(",").append(team.classNumber).append(",").append(team.game).append(",") 93 .append(team.team).append(",") 94 i++ 95 team.player.forEach { 96 sb.append(it).append(",") 97 } 98 bw.write(sb.toString()) 99 bw.newLine() 100 } 101 bw.close() 102 103 controller.log("ファイルを正常に出力しました。") 104 } 105} 106
試したこと
サイトをいくつか参照する中で**~property().bind(他のproperty)**と,他プロパティのバインドを行わなければならないことを知り,
logTextArea.textProperty().bind(task.messageProperty())
という実装をしました。
補足情報(FW/ツールのバージョンなど)
開発言語:Kotlin(1.3.50), Java 8

回答4件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。