From da1cca70e0f04bdfbb4ce8d764774574324fd33c Mon Sep 17 00:00:00 2001 From: Jaap Marsman Date: Fri, 4 Nov 2022 21:14:09 +0800 Subject: [PATCH] Finalising app functions for alpha release --- COPYRIGHTS.md | 4 + .../HTML5FileExchange/HTML5FileExchange.gd | 80 +++++++ godot/Addons/HTML5FileExchange/plugin.cfg | 15 ++ godot/Addons/HTML5FileExchange/plugin.gd | 10 + godot/Results.gd | 30 ++- godot/global_ints.gd | 6 +- godot/main_screen.gd | 22 +- godot/main_screen.tscn | 216 +++++++++++------- godot/project.godot | 5 + 9 files changed, 294 insertions(+), 94 deletions(-) create mode 100644 godot/Addons/HTML5FileExchange/HTML5FileExchange.gd create mode 100644 godot/Addons/HTML5FileExchange/plugin.cfg create mode 100644 godot/Addons/HTML5FileExchange/plugin.gd diff --git a/COPYRIGHTS.md b/COPYRIGHTS.md index 1932b7d..04e19ac 100644 --- a/COPYRIGHTS.md +++ b/COPYRIGHTS.md @@ -14,6 +14,10 @@ The above copyright notice and this permission notice shall be included in all c THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +## Web Export (for Results) + +Uses code from the [HTML5-File-Exchange-for-Godot project](https://github.com/Pukkah/HTML5-File-Exchange-for-Godot). + ## Fonts Portions of this software are copyright © 2022 The FreeType Project (www.freetype.org). All rights reserved. diff --git a/godot/Addons/HTML5FileExchange/HTML5FileExchange.gd b/godot/Addons/HTML5FileExchange/HTML5FileExchange.gd new file mode 100644 index 0000000..637356c --- /dev/null +++ b/godot/Addons/HTML5FileExchange/HTML5FileExchange.gd @@ -0,0 +1,80 @@ +extends Node + +signal read_completed +signal load_completed(image) + +var js_callback = JavaScript.create_callback(self, "load_handler"); +var js_interface; + +func _ready(): + if OS.get_name() == "HTML5" and OS.has_feature('JavaScript'): + _define_js() + js_interface = JavaScript.get_interface("_HTML5FileExchange"); + +func _define_js()->void: + #Define JS script + JavaScript.eval(""" + var _HTML5FileExchange = {}; + _HTML5FileExchange.upload = function(gd_callback) { + canceled = true; + var input = document.createElement('INPUT'); + input.setAttribute("type", "file"); + input.setAttribute("accept", "image/png, image/jpeg, image/webp"); + input.click(); + input.addEventListener('change', event => { + if (event.target.files.length > 0){ + canceled = false;} + var file = event.target.files[0]; + var reader = new FileReader(); + this.fileType = file.type; + // var fileName = file.name; + reader.readAsArrayBuffer(file); + reader.onloadend = (evt) => { // Since here's it's arrow function, "this" still refers to _HTML5FileExchange + if (evt.target.readyState == FileReader.DONE) { + this.result = evt.target.result; + gd_callback(); // It's hard to retrieve value from callback argument, so it's just for notification + } + } + }); + } + """, true) + +func load_handler(_args): + emit_signal("read_completed") + +func load_image(): + if OS.get_name() != "HTML5" or !OS.has_feature('JavaScript'): + return + + js_interface.upload(js_callback); + + yield(self, "read_completed") + + var imageType = js_interface.fileType; + var imageData = JavaScript.eval("_HTML5FileExchange.result", true) # interface doesn't work as expected for some reason + + var image = Image.new() + var image_error + match imageType: + "image/png": + image_error = image.load_png_from_buffer(imageData) + "image/jpeg": + image_error = image.load_jpg_from_buffer(imageData) + "image/webp": + image_error = image.load_webp_from_buffer(imageData) + var invalidType: + print("Unsupported file format - %s." % invalidType) + return + + if image_error: + print("An error occurred while trying to display the image.") + + emit_signal("load_completed", image) + +func save_image(image:Image, fileName:String = "export.png")->void: + if OS.get_name() != "HTML5" or !OS.has_feature('JavaScript'): + return + + image.clear_mipmaps() + var buffer = image.save_png_to_buffer() + JavaScript.download_buffer(buffer, fileName) diff --git a/godot/Addons/HTML5FileExchange/plugin.cfg b/godot/Addons/HTML5FileExchange/plugin.cfg new file mode 100644 index 0000000..1354542 --- /dev/null +++ b/godot/Addons/HTML5FileExchange/plugin.cfg @@ -0,0 +1,15 @@ +[plugin] + +name="HTML5 File Exchange" +description="Open/Import and Save/Export files in HTML5 environment. +Supported input - *.png, *.jpeg, *.webp +Supported output - *.png + +Usage: +Get file from User - +var image:Image = yield(HTML5File.get_image(), \"completed\") +Send file to User - +HTML5File.save_image(image:Image, fileName:String = \"export\")" +author="laame" +version="0.1.1" +script="plugin.gd" diff --git a/godot/Addons/HTML5FileExchange/plugin.gd b/godot/Addons/HTML5FileExchange/plugin.gd new file mode 100644 index 0000000..2d9467f --- /dev/null +++ b/godot/Addons/HTML5FileExchange/plugin.gd @@ -0,0 +1,10 @@ +tool +extends EditorPlugin + + +func _enter_tree(): + add_autoload_singleton("HTML5File", "res://addons/HTML5FileExchange/HTML5FileExchange.gd") + + +func _exit_tree(): + remove_autoload_singleton("HTML5File") diff --git a/godot/Results.gd b/godot/Results.gd index 56e7d21..31714c3 100644 --- a/godot/Results.gd +++ b/godot/Results.gd @@ -7,16 +7,30 @@ onready var global_ints = $"/root/GlobalInts" func _ready(): pass - -func Download_File(_img,_filename): - var buf = _img.save_png_to_buffer() - JavaScript.download_buffer(buf,_filename+".png") - - -func _on_SaveReport_pressed(): -# Download_File(results, results) +func _process(delta): +# $"%SaveReport".visible = true +# $"%BackMainMenu".visible = true pass +func _on_SaveReport_pressed(): + $"%SaveReport".visible = false + $"%BackMainMenu".visible = false + + # WIP: Web Version +# var file = File.new() +# file.open("res://screenshot.png", File.READ) +# var base_64_data = Marshalls.raw_to_base64(file.get_buffer(file.get_len())) +# var url = "data:image/jpg;base64,"+base_64_data +# var comand = "var a = document.createElement('a'); a.href = '" + url + "'; a.setAttribute( 'download' , 'filename.jpg' ); a.click();" + +# JavaScript.eval(comand, true) + + # WIP: Non-Web Version + var image = get_viewport().get_texture().get_data() + + image.flip_y() + image.save_png("user://results.png") + func _on_BackMainMenu_pressed(): global_ints.reset_all_vars() diff --git a/godot/global_ints.gd b/godot/global_ints.gd index f9d9b70..1b087e0 100644 --- a/godot/global_ints.gd +++ b/godot/global_ints.gd @@ -1,13 +1,11 @@ extends Node -# For testing purposes - make the intervals 4 seconds instead of 20 - var date var ddmmyyyy var total_observed_time : int -var timer_duration : int = 20 +var timer_duration : int = 6 var observation_minutes : int = 1 @@ -61,7 +59,7 @@ var four_behaviour_percent : int var five_behaviour_percent : int func reset_all_vars(): - observation_minutes = 1 + observation_minutes = 15 generate_results = false diff --git a/godot/main_screen.gd b/godot/main_screen.gd index adbb24a..0b934ef 100644 --- a/godot/main_screen.gd +++ b/godot/main_screen.gd @@ -49,6 +49,7 @@ func _ready(): refresh_descriptors() $"StartScreen".visible = true $"%NameChangePanel".visible = true + $"%InstructionScreen".visible = true $"%WarningLabel".visible = false $"ObservationWindow".visible = false $"Results".visible = false @@ -70,29 +71,29 @@ func _process(_delta): date_time_display.text = str(date_time.hour, ":", date_time.minute) -func _on_MinuteMinus_pressed() -> void: +func _on_MinuteMinus_pressed(): if global_ints.observation_minutes >= 2: global_ints.observation_minutes -= 1 minute_label.text = str(global_ints.observation_minutes) -func _on_MinutePlus_pressed() -> void: +func _on_MinutePlus_pressed(): if global_ints.observation_minutes < 60: global_ints.observation_minutes += 1 minute_label.text = str(global_ints.observation_minutes) -func _on_Manual_pressed() -> void: +func _on_Manual_pressed(): var _error = OS.shell_open("https://www.internationalsengroup.org/resources/time-sampling-form/") -func _on_PupilName_pressed() -> void: +func _on_PupilName_pressed(): $"%NameLine".text = global_ints.observed_person_name $"%InstructionPanel".visible = false $"%NameChangePanel".visible = true -func _on_Start_pressed() -> void: +func _on_Start_pressed(): $"StartScreen".visible = false refresh_descriptors() $"ObservationWindow".visible = true @@ -130,3 +131,14 @@ func _on_Start_pressed() -> void: func _on_ChangeItems_pressed(): $"EditScreen".visible = true + + +func _on_InsOkButton_pressed(): + $"%InstructionScreen".visible = false + + +func _on_MinuteMinus_button_down(): +# if global_ints.observation_minutes >= 2: +# global_ints.observation_minutes -= 1 +# minute_label.text = str(global_ints.observation_minutes) + pass diff --git a/godot/main_screen.tscn b/godot/main_screen.tscn index 72fb340..ae584aa 100644 --- a/godot/main_screen.tscn +++ b/godot/main_screen.tscn @@ -75,7 +75,6 @@ __meta__ = { } [node name="StartScreen" type="CanvasLayer" parent="."] -visible = false [node name="InstructionPanel" type="Panel" parent="StartScreen"] unique_name_in_owner = true @@ -136,10 +135,10 @@ __meta__ = { [node name="CurrentTime" type="Label" parent="StartScreen/InstructionPanel"] unique_name_in_owner = true -margin_left = 1200.0 -margin_top = 510.0 -margin_right = 1279.0 -margin_bottom = 533.0 +margin_left = 1196.0 +margin_top = 506.0 +margin_right = 1275.0 +margin_bottom = 529.0 custom_colors/font_color = Color( 0, 0, 0, 1 ) custom_fonts/font = SubResource( 4 ) text = "99:99" @@ -155,7 +154,7 @@ margin_top = -197.0 margin_right = 322.5 margin_bottom = -163.0 theme = ExtResource( 1 ) -text = "How long will you observe? (in minutes)" +text = "How long will you observe for? (in minutes)" align = 1 [node name="ObservationItemsTitle" type="Label" parent="StartScreen/InstructionPanel"] @@ -473,7 +472,7 @@ unique_name_in_owner = true margin_right = 539.0 margin_bottom = 34.0 theme = ExtResource( 1 ) -text = "Observed Individual First Name:" +text = "Who are you observing? (First name)" [node name="WarningLabel" type="Label" parent="StartScreen/NameChangePanel/NameContainer"] unique_name_in_owner = true @@ -496,7 +495,7 @@ margin_top = 114.0 margin_right = 539.0 margin_bottom = 148.0 theme = ExtResource( 1 ) -text = "Your Full Name:" +text = "What is your name?" [node name="ObserverLine" type="LineEdit" parent="StartScreen/NameChangePanel/NameContainer"] unique_name_in_owner = true @@ -511,7 +510,7 @@ margin_top = 190.0 margin_right = 539.0 margin_bottom = 224.0 theme = ExtResource( 1 ) -text = "Observed during which Activity/Lesson:" +text = "What Activity/Lesson are you observing?" [node name="ObservedActivity" type="LineEdit" parent="StartScreen/NameChangePanel/NameContainer"] unique_name_in_owner = true @@ -527,6 +526,50 @@ margin_bottom = 300.0 theme = ExtResource( 1 ) text = "OK" +[node name="InstructionScreen" type="Panel" parent="StartScreen"] +unique_name_in_owner = true +visible = false +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +margin_left = -640.0 +margin_top = -254.0 +margin_right = 640.0 +margin_bottom = 359.0 +custom_styles/panel = SubResource( 1 ) +script = ExtResource( 4 ) + +[node name="InstructionContainer" type="VBoxContainer" parent="StartScreen/InstructionScreen"] +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +margin_left = -385.0 +margin_top = -258.0 +margin_right = 385.0 +margin_bottom = 258.0 + +[node name="Instructions" type="Label" parent="StartScreen/InstructionScreen/InstructionContainer"] +margin_right = 770.0 +margin_bottom = 478.0 +theme = ExtResource( 1 ) +text = "The Time Sampling Form is a guided observation tool. You set the length of your observation in minutes. + +Every 20 seconds you will click the button that corresponds with the behaviour you see at that time. The buttons to select the observed behaviour will be visible in the final 5 seconds of each 20 second interval. + +Use the \"Manual\" button on the main screen to read more about this tool and possible use cases. + +For first time users: It is recommended to do a one minute trial to gain an understanding of the system. I hope you find this tool useful!" +autowrap = true + +[node name="InsOkButton" type="Button" parent="StartScreen/InstructionScreen/InstructionContainer"] +margin_top = 482.0 +margin_right = 770.0 +margin_bottom = 516.0 +theme = ExtResource( 1 ) +text = "OK" + [node name="EditScreen" type="CanvasLayer" parent="."] unique_name_in_owner = true visible = false @@ -779,6 +822,7 @@ margin_bottom = 649.0 text = "Clear All" [node name="ObservationWindow" type="CanvasLayer" parent="."] +visible = false script = ExtResource( 7 ) [node name="Panel" type="Panel" parent="ObservationWindow"] @@ -788,6 +832,9 @@ margin_top = 188.0 margin_bottom = 1.0 theme = ExtResource( 1 ) custom_styles/panel = SubResource( 7 ) +__meta__ = { +"_edit_lock_": true +} [node name="BehaviourButtons" type="HBoxContainer" parent="ObservationWindow/Panel"] anchor_left = 0.5 @@ -910,10 +957,14 @@ theme = ExtResource( 1 ) text = "Seconds remaining" [node name="Button" type="Button" parent="ObservationWindow/Panel"] -margin_left = 582.0 -margin_top = 13.0 -margin_right = 700.0 -margin_bottom = 47.0 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +margin_left = -74.5 +margin_top = -231.5 +margin_right = 74.5 +margin_bottom = -191.5 text = "Stop Early" [node name="TimerBar" type="ProgressBar" parent="ObservationWindow/Panel"] @@ -998,6 +1049,7 @@ unique_name_in_owner = true wait_time = 20.0 [node name="Results" type="CanvasLayer" parent="."] +visible = false script = ExtResource( 9 ) [node name="Panel" type="Panel" parent="Results"] @@ -1006,19 +1058,24 @@ anchor_bottom = 1.0 margin_top = -3.0 theme = ExtResource( 1 ) custom_styles/panel = SubResource( 8 ) +__meta__ = { +"_edit_lock_": true +} [node name="SaveReport" type="Button" parent="Results/Panel"] -margin_left = 1042.0 -margin_top = 683.0 -margin_right = 1271.0 -margin_bottom = 717.0 +unique_name_in_owner = true +margin_left = 1011.0 +margin_top = 675.0 +margin_right = 1270.0 +margin_bottom = 715.0 text = "Save Report as PNG" [node name="BackMainMenu" type="Button" parent="Results/Panel"] +unique_name_in_owner = true margin_left = 11.0 -margin_top = 676.0 +margin_top = 675.0 margin_right = 155.0 -margin_bottom = 710.0 +margin_bottom = 715.0 text = "Restart" [node name="FullResult" type="Label" parent="Results/Panel"] @@ -1032,106 +1089,110 @@ text = "Can this be multi-line? Or does text-wrap work? Can I get integers in? Must be able to, concatenate." autowrap = true +__meta__ = { +"_edit_lock_": true +} [node name="ObsBar1" type="ProgressBar" parent="Results/Panel"] unique_name_in_owner = true -margin_left = 271.0 +margin_left = 348.0 margin_top = 422.0 -margin_right = 1063.0 +margin_right = 1140.0 margin_bottom = 447.0 size_flags_horizontal = 3 step = 1.0 percent_visible = false +[node name="ObsBar2" type="ProgressBar" parent="Results/Panel"] +unique_name_in_owner = true +margin_left = 348.0 +margin_top = 452.0 +margin_right = 1140.0 +margin_bottom = 477.0 +size_flags_horizontal = 3 +step = 1.0 +percent_visible = false + +[node name="ObsBar3" type="ProgressBar" parent="Results/Panel"] +unique_name_in_owner = true +margin_left = 348.0 +margin_top = 482.0 +margin_right = 1140.0 +margin_bottom = 507.0 +size_flags_horizontal = 3 +step = 1.0 +percent_visible = false + +[node name="ObsBar4" type="ProgressBar" parent="Results/Panel"] +unique_name_in_owner = true +margin_left = 348.0 +margin_top = 512.0 +margin_right = 1140.0 +margin_bottom = 537.0 +size_flags_horizontal = 3 +step = 1.0 +percent_visible = false + +[node name="ObsBar5" type="ProgressBar" parent="Results/Panel"] +unique_name_in_owner = true +margin_left = 348.0 +margin_top = 542.0 +margin_right = 1140.0 +margin_bottom = 567.0 +size_flags_horizontal = 3 +step = 1.0 +percent_visible = false + [node name="GridContainer" type="GridContainer" parent="Results/Panel"] anchor_left = 0.5 anchor_top = 0.5 anchor_right = 0.5 anchor_bottom = 0.5 -margin_left = -423.0 +margin_left = -517.0 margin_top = 60.5 -margin_right = 423.0 -margin_bottom = 181.5 -columns = 2 +margin_right = -302.0 +margin_bottom = 206.5 [node name="ObsTitle1" type="Label" parent="Results/Panel/GridContainer"] unique_name_in_owner = true -margin_right = 792.0 +margin_right = 50.0 margin_bottom = 26.0 custom_fonts/font = SubResource( 12 ) text = "Item1" [node name="ObsTitle2" type="Label" parent="Results/Panel/GridContainer"] unique_name_in_owner = true -margin_left = 796.0 -margin_right = 846.0 -margin_bottom = 26.0 +margin_top = 30.0 +margin_right = 50.0 +margin_bottom = 56.0 custom_fonts/font = SubResource( 12 ) text = "Item2" -[node name="ObsBar2" type="ProgressBar" parent="Results/Panel/GridContainer"] -unique_name_in_owner = true -margin_top = 30.0 -margin_right = 792.0 -margin_bottom = 31.0 -size_flags_horizontal = 3 -step = 1.0 -percent_visible = false - [node name="ObsTitle3" type="Label" parent="Results/Panel/GridContainer"] unique_name_in_owner = true -margin_left = 796.0 -margin_top = 30.0 -margin_right = 846.0 -margin_bottom = 56.0 +margin_top = 60.0 +margin_right = 50.0 +margin_bottom = 86.0 custom_fonts/font = SubResource( 12 ) text = "Item3" -[node name="ObsBar3" type="ProgressBar" parent="Results/Panel/GridContainer"] -unique_name_in_owner = true -margin_top = 60.0 -margin_right = 792.0 -margin_bottom = 61.0 -size_flags_horizontal = 3 -step = 1.0 -percent_visible = false - [node name="ObsTitle4" type="Label" parent="Results/Panel/GridContainer"] unique_name_in_owner = true -margin_left = 796.0 -margin_top = 60.0 -margin_right = 846.0 -margin_bottom = 86.0 +margin_top = 90.0 +margin_right = 50.0 +margin_bottom = 116.0 custom_fonts/font = SubResource( 12 ) text = "Item4" -[node name="ObsBar4" type="ProgressBar" parent="Results/Panel/GridContainer"] -unique_name_in_owner = true -margin_top = 90.0 -margin_right = 792.0 -margin_bottom = 91.0 -size_flags_horizontal = 3 -step = 1.0 -percent_visible = false - [node name="ObsTitle5" type="Label" parent="Results/Panel/GridContainer"] unique_name_in_owner = true -margin_left = 796.0 -margin_top = 90.0 -margin_right = 846.0 -margin_bottom = 116.0 +margin_top = 120.0 +margin_right = 50.0 +margin_bottom = 146.0 custom_fonts/font = SubResource( 12 ) text = "Item5" -[node name="ObsBar5" type="ProgressBar" parent="Results/Panel/GridContainer"] -unique_name_in_owner = true -margin_top = 120.0 -margin_right = 792.0 -margin_bottom = 121.0 -size_flags_horizontal = 3 -step = 1.0 -percent_visible = false - +[connection signal="button_down" from="StartScreen/InstructionPanel/MinuteBox/MinuteMinus" to="." method="_on_MinuteMinus_button_down"] [connection signal="pressed" from="StartScreen/InstructionPanel/MinuteBox/MinuteMinus" to="." method="_on_MinuteMinus_pressed"] [connection signal="pressed" from="StartScreen/InstructionPanel/MinuteBox/MinutePlus" to="." method="_on_MinutePlus_pressed"] [connection signal="pressed" from="StartScreen/InstructionPanel/BottomButtons/Manual" to="." method="_on_Manual_pressed"] @@ -1139,6 +1200,7 @@ percent_visible = false [connection signal="pressed" from="StartScreen/InstructionPanel/BottomButtons/PupilName" to="." method="_on_PupilName_pressed"] [connection signal="pressed" from="StartScreen/InstructionPanel/BottomButtons/Start" to="." method="_on_Start_pressed"] [connection signal="pressed" from="StartScreen/NameChangePanel/NameContainer/OkButton" to="StartScreen/NameChangePanel" method="_on_OkButton_pressed"] +[connection signal="pressed" from="StartScreen/InstructionScreen/InstructionContainer/InsOkButton" to="." method="_on_InsOkButton_pressed"] [connection signal="pressed" from="EditScreen/Panel/CancelButton" to="EditScreen" method="_on_CancelButton_pressed"] [connection signal="pressed" from="EditScreen/Panel/OKButton" to="EditScreen" method="_on_OKButton_pressed"] [connection signal="pressed" from="EditScreen/Panel/ClearAll" to="EditScreen" method="_on_ClearAll_pressed"] diff --git a/godot/project.godot b/godot/project.godot index 02b3c14..141a5d9 100644 --- a/godot/project.godot +++ b/godot/project.godot @@ -19,6 +19,7 @@ config/macos_native_icon="res://Assets/icons/main_icon.icns" [autoload] GlobalInts="*res://global_ints.gd" +HTML5File="*res://addons/HTML5FileExchange/HTML5FileExchange.gd" [display] @@ -27,6 +28,10 @@ window/size/height=720 window/stretch/mode="2d" window/stretch/aspect="keep" +[editor_plugins] + +enabled=PoolStringArray( "res://addons/HTML5FileExchange/plugin.cfg" ) + [gui] common/drop_mouse_on_gui_input_disabled=true