How to get an image that dragged and dropped from the browser to the DropArea QML component - drag-and-drop

I'm using the DropArea component to transfer files using the Drag&Drop mechanism to my application. The following test code works fine with files located on the system:
import QtQuick 2.15
import QtQuick.Window 2.15
Window {
id: root
width: 640
height: 480
visible: true
DropArea {
id: dropArea
anchors.fill: parent
onEntered: {
drag.accepted = drag.hasUrls
}
onDropped: {
// Use files through drop.urls
drop.accept()
}
}
}
In onEntered I accept the DragEvent if it contains urls, and in onDropped I can use urls to work with dropped files.
But also I need to accept images from the browser through DropArea. At the same time, since the images dragged from the browser do not exist in the file system, I expect to receive raw image data with which I create the image file myself.
The problem is that the DragEvent drop from dropped signal does not have such data. This can be verified with the following logging:
onDropped: {
console.log("drop.urls: " + drop.urls)
console.log("drop.html: " + drop.html)
console.log("drop.text: " + drop.text)
console.log("-----------------------")
for (var i = 0; i < drop.formats.length; ++i) {
console.log(drop.formats[i] + ": " + drop.getDataAsString(drop.formats[i]))
}
}
that will give the following information (dragged and dropped the Qt logo image from the documentation):
qml: drop.urls: https://doc.qt.io/
qml: drop.html: <html>
<body>
<!--StartFragment--><img src="https://doc.qt.io/style/qt-logo-documentation.svg" alt="Qt documentation"><!--EndFragment-->
</body>
</html>
qml: drop.text: https://doc.qt.io/
qml: -----------------------
qml: application/x-qt-windows-mime;value="DragContext":
qml: application/x-qt-windows-mime;value="DragImageBits": ?
qml: application/x-qt-windows-mime;value="chromium/x-renderer-taint":
qml: application/x-qt-windows-mime;value="FileGroupDescriptorW":
qml: application/x-qt-windows-mime;value="FileContents":
qml: text/x-moz-url: h
qml: text/uri-list: https://doc.qt.io/
qml: application/x-qt-windows-mime;value="UniformResourceLocatorW": h
qml: text/plain: https://doc.qt.io/
qml: text/html: <html>
<body>
<!--StartFragment--><img src="https://doc.qt.io/style/qt-logo-documentation.svg" alt="Qt documentation"><!--EndFragment-->
</body>
</html>
Among the available properties (including those obtained through formats), there is no raw image data. drop.html provides useful information about the address of the dropped image, but is it really the only way to get an image is to download it using the received link?
I also thought about whether it is possible to somehow get QMimeData so that it can call imageData() and get the image in this way. I found a similar transfer of mime data to QML from the Krita developers (see DeclarativeMimeData* const m_data, which they define as Q_PROPERTY), but I'm not sure that this is the easiest, and most importantly, working way.
So to sum it up, is there a way to get the raw data of an image dragged and dropped from a browser or the image itself as a QImage, using the standard QML DropArea component?

Before going to C++ and QNetworkAccessManager there are extra things you can do in QML to validate whether you indeed have an image.
The following demonstrates that we can either:
check the contents of the drop.formats
run an XMLHTTPRequest "HEAD" request
The latter is particularly useful because we can infer the mime type from the content type, and, if available, determine how big something is by reading the content length.
Also, if you wish to have an in-memory copy of the image, you can create a second XMLHttpRequest. This time you can set "GET" and set responseType to "arraybuffer".
DropArea {
id: dropArea
anchors.fill: parent
property url dropUrl
property string contentType
property bool isImage
property int contentLength
onDropped: function (drop) {
console.log("formats: ", JSON.stringify(drop.formats));
if (!drop.hasUrls) return;
let xhr = new XMLHttpRequest();
dropUrl = drop.urls[0];
console.log("dropUrl: ", dropUrl);
xhr.open("HEAD", dropUrl, false);
xhr.send();
contentType = xhr.getResponseHeader("Content-Type");
console.log("contentType: ", contentType);
isImage = contentType.startsWith("image/");
contentLength = xhr.getResponseHeader("Content-Length") ?? 0;
console.log("contentLength: ", contentLength);
let xhr2 = new XMLHttpRequest();
xhr2.open("GET", dropUrl, false);
xhr2.responseType = "arraybuffer";
xhr2.send();
let data = xhr2.response;
console.log(data.byteLength);
}
}

Related

How to reference a gist stylesheet in another gist?

I created a gist that has a html page and a css file. How would I reference the css file in the html page?
I tried a few ways but none of them seems working. The html page is at: https://gist.github.com/yuipcheng/943286be35fd690f499d59534281a6c5
Viewing page: https://gistpreview.github.io/?943286be35fd690f499d59534281a6c5
gistpreview.github.io loads the html with Javascript by using the Github Gist API. From the source code it just writes the html content. So, it doesn't deal with relative links that may link to other css files.
You would need to :
fork the gistpreview project
after the html rendering (document.write), iterate over the link tags and check if they are present in the files field of the API result
load the css content dynamically if matching
I've tested this method above using this fork.
Example for you gist: https://bertrandmartel.github.io/gistpreview.github.io/?943286be35fd690f499d59534281a6c5
Code before :
var content = info.files[fileName].content;
document.write(content);
Code after :
var content = info.files[fileName].content;
document.write(content);
//load links
var links = document.querySelectorAll("link");
for (let [key, value] of Object.entries(info.files)) {
for (var i = 0; i < links.length; i++) {
var href = links[i].getAttribute("href").replace(/^\/|\/$/g, '');
if (value.filename === href && value.type === "text/css") {
console.log("load file " + value.filename);
var style = document.createElement('style');
style.type = 'text/css';
style.innerHTML = value.content;
document.getElementsByTagName('head')[0].appendChild(style);
}
}
}

Show loading Screen while Image is loading in wicket

I'am populating a ListView with images.
In pseudocode:
populateItem(model){
load base64 from database
image.setDefaultModel(base64)
The image is just a webcomponent and in html it is just <img src="">
How can i show a indicator while the image is loaded?.
I first thought of adding IAjaxIndicatorAware but this triggers the indicator when the image is doing an AjaxRequest.
Since you seem to load and display the image as a Base64 src it will directly get send in the html response and not loaded later (in contrast to images with a src that links to another URI).
You could wrap the image in an AjaxLazyLoadPanel.
This will first display an AjaxIndicator while the content is generated and get later replaced by the actual loaded content once it is done loading/generating:
edit
I got an Exception : Component must be applied to a tag of type [img].
i didn't consider that problem. AjaxLazyLoadPanel allways uses a <div> as a html tag to display the component it loads. To display a base 64 image you would need to wrap it in another Panel:
public class Base64ImagePanel extends Panel {
public Base64ImagePanel(String wicketId, String base64Data, String contentType) {
super(wicketId);
WebMarkupContainer image = new WebMarkupContainer("image") {
protected void onComponentTag(ComponentTag tag) {
super.onComponentTag(tag);
checkComponentTag(tag, "img");
tag.put("src", "data:" + contentType + ";base64," + base64Data);
}
}
add(image);
}
}
Base64ImagePanel.html:
<wicket:panel>
<img wicket:id="image"></img>
</wicket:panel>
And then use that wrapper Panel in the AjaxLazyLoadPanel:
add(new AjaxLazyLoadPanel("imageLazyLoad") {
#Override
public Component getLazyLoadComponent(String id) {
//load your actual base64 from database, i use some example Strings for demonstration in the following line
Base64ImagePanel imagePanel = new Base64ImagePanel(id, "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==", "image/png");
return imagePanel;
}
});

Mapbox GL Popup .setDOMContent example

I'm trying to create a customized button to appear on a pop up which generates a dynamic link (a URL). I don't seem to be able to do this via the .setHTML because of the timing, can't bind a button to a function at runtime. So I thought I'd try the newish .setDOMContent
There's zero information online as to how this feature works. I'm wondering if anyone has an example of this where a button is added to the popup that can run a function and send data.
Here's my very poor attempt at setting this up.
This function creates the popup
function GameObjectPopup(myObject) {
var features = map.queryRenderedFeatures(myObject.point, {
layers: ['seed']
});
if (!features.length) {
return;
}
var feature = features[0];
// Populate the popup and set its coordinates
// based on the feature found.
var popup = new mapboxgl.Popup()
.setLngLat(feature.geometry.coordinates)
.setHTML(ClickedGameObject(feature))
.setDOMContent(ClickedGameObject2(feature))
.addTo(map);
};
This function adds the html via the .setHTML
function ClickedGameObject(feature){
console.log("clicked on button");
var html = '';
html += "<div id='mapboxgl-popup'>";
html += "<h2>" + feature.properties.title + "</h2>";
html += "<p>" + feature.properties.description + "</p>";
html += "<button class='content' id='btn-collectobj' value='Collect'>";
html += "</div>";
return html;
}
This function wants to add the DOM content via the .setDOMContent
function ClickedGameObject2(feature){
document.getElementById('btn-collectobj').addEventListener('click', function()
{
console.log("clicked a button");
AddGameObjectToInventory(feature.geometry.coordinates);
});
}
I'm trying to pipe the variable from features.geometry.coordinates into the function AddGameObjectToInventory()
the error I'm getting when clicking on an object (so as popup is being generated)
Uncaught TypeError: Cannot read property 'addEventListener' of null
Popup#setHTML takes a string that represents some HTML content:
var str = "<h1>Hello, World!</h1>"
popup.setHTML(str);
while Popup#setDOMContent takes actual HTML nodes. i.e:
var h1 = document.createElement('h1');
h1.innerHTML="Hello, World";
popup.setDOMContent(h1);
both of those code snippets would result in identical Popup HTML contents. You wouldn't want to use both methods on a single popup because they are two different ways to do the same thing.
The problem in the code you shared is that you're trying to use the setDOMContent to add an event listener to your button, but you don't need to access the Popup object to add the event listener once the popup DOM content has been added to the map. Here is a working version of what I think you're trying to do: https://jsfiddle.net/h4j554sk/

Upload Images and files with TinyMCE

In my symfony2 project I use TidyMCE in my textareas, to be able to insert news from the backend. News can have images in the content or files links to pdf files to display. I am new to this and I can not upload the images or files, so that I can search the different folders and once selected a copy is made on the server.
I've been looking at a lot of comments, but I'm kind of bundled up. I have seen on the web of tinymceel following code:
Basic Local File Picker
tinymce.init({
selector: '#editor',
plugins: 'image code',
toolbar: 'undo redo | link image | code',
// enable title field in the Image dialog
image_title: true,
// enable automatic uploads of images represented by blob or data URIs
automatic_uploads: true,
// URL of our upload handler (for more details check: https://www.tinymce.com/docs/configure/file-image-upload/#images_upload_url)
images_upload_url: 'postAcceptor.php',
// here we add custom filepicker only to Image dialog
file_picker_types: 'image',
// and here's our custom image picker
file_picker_callback: function(cb, value, meta) {
var input = document.createElement('input');
input.setAttribute('type', 'file');
input.setAttribute('accept', 'image/*');
// Note: In modern browsers input[type="file"] is functional without
// even adding it to the DOM, but that might not be the case in some older
// or quirky browsers like IE, so you might want to add it to the DOM
// just in case, and visually hide it. And do not forget do remove it
// once you do not need it anymore.
input.onchange = function() {
var file = this.files[0];
// Note: Now we need to register the blob in TinyMCEs image blob
// registry. In the next release this part hopefully won't be
// necessary, as we are looking to handle it internally.
var id = 'blobid' + (new Date()).getTime();
var blobCache = tinymce.activeEditor.editorUpload.blobCache;
var blobInfo = blobCache.create(id, file);
blobCache.add(blobInfo);
// call the callback and populate the Title field with the file name
cb(blobInfo.blobUri(), { title: file.name });
};
input.click();
}
});
PHP Upload Handler
<?php
/*******************************************************
* Only these origins will be allowed to upload images *
******************************************************/
$accepted_origins = array("http://localhost", "http://192.168.1.1", "http://example.com");
/*********************************************
* Change this line to set the upload folder *
*********************************************/
$imageFolder = "images/";
reset ($_FILES);
$temp = current($_FILES);
if (is_uploaded_file($temp['tmp_name'])){
if (isset($_SERVER['HTTP_ORIGIN'])) {
// same-origin requests won't set an origin. If the origin is set, it must be valid.
if (in_array($_SERVER['HTTP_ORIGIN'], $accepted_origins)) {
header('Access-Control-Allow-Origin: ' . $_SERVER['HTTP_ORIGIN']);
} else {
header("HTTP/1.0 403 Origin Denied");
return;
}
}
/*
If your script needs to receive cookies, set images_upload_credentials : true in
the configuration and enable the following two headers.
*/
// header('Access-Control-Allow-Credentials: true');
// header('P3P: CP="There is no P3P policy."');
// Sanitize input
if (preg_match("/([^\w\s\d\-_~,;:\[\]\(\).])|([\.]{2,})/", $temp['name'])) {
header("HTTP/1.0 500 Invalid file name.");
return;
}
// Verify extension
if (!in_array(strtolower(pathinfo($temp['name'], PATHINFO_EXTENSION)), array("gif", "jpg", "png"))) {
header("HTTP/1.0 500 Invalid extension.");
return;
}
// Accept upload if there was no origin, or if it is an accepted origin
$filetowrite = $imageFolder . $temp['name'];
move_uploaded_file($temp['tmp_name'], $filetowrite);
// Respond to the successful upload with JSON.
// Use a location key to specify the path to the saved image resource.
// { location : '/your/uploaded/image/file'}
echo json_encode(array('location' => $filetowrite));
} else {
// Notify editor that the upload failed
header("HTTP/1.0 500 Server Error");
}
?>
But I do not quite understand where to put the postAcceptor.php or referred to with {location: '/ your / uploaded / image / file'}.
I'm a little lost, please thank all the possible help
postacceptor is your server side method for accepting the file and uploading it in your server. I am using mvc so I created a custom route "ImageUpload". and I use that instead.
location part is the expected return from your method. this will replace the file location/referrer a.k.a src tag when you use images_reuse_filename = true.
I don't use symphony but according to your code you would put the postAcceptor file in the same directory as form

show image on client before upload on server in gwt

I would like to show an image and its dimensions on the client before uploading it to the server. Whenever I try to create an Image widget of gwt ext, it doesn't accept a local file (on the file system). It only accepts http requests. I also tried String img = GWT.getHostPageBaseURL() + "image_name" and the replace function but with same result. Finally I moved to ImagePreloader but its also needs a URL.
ImagePreloader.load("URL of image", new ImageLoadHandler()
{
public void imageLoaded(ImageLoadEvent event)
{
if (event.isLoadFailed())
Window.alert("Image " + event.getImageUrl() + "failed to load.");
else
Window.alert("Image dimensions: " + event.getDimensions().getWidth() + " x " + event.getDimensions().getHeight());
}
});
Can someone suggest a solution that doesn't include uploading the image first?
Have a look at this related question and answer:
Preview an image before it is uploaded
You can include it using JSNI. I'm now aware of a solution within GWT.
Just found gwtupload which claims to be able to preview images before uploading them.
Please take a look at the sample JS code below:
function readURL(input) {
if (input.files && input.files[0]) {
var reader = new FileReader();
reader.onload = function (e) {
$('#blah').attr('src', e.target.result);
}
reader.readAsDataURL(input.files[0]);
}
}
$("#imgInp").change(function(){
readURL(this);
});
and the associated HTML:
<form id="form1" runat="server">
<input type='file' id="imgInp" />
<img id="blah" src="#" alt="your image" />
</form>