Skip to content

Commit 6cb36e0

Browse files
committed
Create node on file drop in canvas
1 parent c2b8582 commit 6cb36e0

File tree

9 files changed

+164
-10
lines changed

9 files changed

+164
-10
lines changed

Orange/widgets/data/owfile.py

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@
22
import logging
33
from itertools import chain
44
from urllib.parse import urlparse
5-
from typing import List
5+
from typing import List, Dict, Any
66

77
import numpy as np
88
from AnyQt.QtWidgets import \
99
QStyle, QComboBox, QMessageBox, QGridLayout, QLabel, \
1010
QLineEdit, QSizePolicy as Policy, QCompleter
11-
from AnyQt.QtCore import Qt, QTimer, QSize
11+
from AnyQt.QtCore import Qt, QTimer, QSize, QUrl
12+
13+
from orangewidget.workflow.drophandler import SingleUrlDropHandler
1214

1315
from Orange.data.table import Table, get_sample_datasets_dir
1416
from Orange.data.io import FileFormat, UrlReader, class_from_qualified_name
@@ -28,7 +30,6 @@
2830
# module's namespace so that old saved settings still work
2931
from Orange.widgets.utils.filedialogs import RecentPath
3032

31-
3233
log = logging.getLogger(__name__)
3334

3435

@@ -571,5 +572,34 @@ def workflowEnvChanged(self, key, value, oldvalue):
571572
self.update_file_list(key, value, oldvalue)
572573

573574

575+
class OWFileDropHandler(SingleUrlDropHandler):
576+
WIDGET = OWFile
577+
578+
def canDropUrl(self, url: QUrl) -> bool:
579+
if url.isLocalFile():
580+
try:
581+
FileFormat.get_reader(url.toLocalFile())
582+
return True
583+
except Exception: # noqa # pylint:disable=broad-except
584+
return False
585+
else:
586+
return url.scheme().lower() in ("http", "https", "ftp")
587+
588+
def parametersFromUrl(self, url: QUrl) -> Dict[str, Any]:
589+
if url.isLocalFile():
590+
path = url.toLocalFile()
591+
r = RecentPath(os.path.abspath(path), None, None,
592+
os.path.basename(path))
593+
return {
594+
"recent_paths": [r],
595+
"source": OWFile.LOCAL_FILE,
596+
}
597+
else:
598+
return {
599+
"recent_urls": [url.toString()],
600+
"source": OWFile.URL,
601+
}
602+
603+
574604
if __name__ == "__main__": # pragma: no cover
575605
WidgetPreview(OWFile).run()

Orange/widgets/data/owpythonscript.py

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from functools import reduce
99
from unittest.mock import patch
1010

11-
from typing import Optional, List, TYPE_CHECKING
11+
from typing import Optional, List, Dict, Any, TYPE_CHECKING
1212

1313
from AnyQt.QtWidgets import (
1414
QPlainTextEdit, QListView, QSizePolicy, QMenu, QSplitter, QLineEdit,
@@ -20,9 +20,12 @@
2020
QSyntaxHighlighter, QTextCharFormat, QTextCursor, QKeySequence,
2121
)
2222
from AnyQt.QtCore import (
23-
Qt, QRegularExpression, QByteArray, QItemSelectionModel, QSize
23+
Qt, QRegularExpression, QByteArray, QItemSelectionModel, QSize,
24+
QMimeDatabase
2425
)
2526

27+
from orangewidget.workflow.drophandler import SingleFileDropHandler
28+
2629
from Orange.data import Table
2730
from Orange.base import Learner, Model
2831
from Orange.util import interleave
@@ -800,5 +803,32 @@ def migrate_settings(cls, settings, version):
800803
settings["scriptLibrary"] = library
801804

802805

806+
class OWPythonScriptDropHandler(SingleFileDropHandler):
807+
WIDGET = OWPythonScript
808+
809+
def canDropFile(self, path: str) -> bool:
810+
md = QMimeDatabase()
811+
mt = md.mimeTypeForFile(path)
812+
return mt.inherits("text/x-python")
813+
814+
def parametersFromFile(self, path: str) -> Dict[str, Any]:
815+
with open(path, "rt") as f:
816+
content = f.read()
817+
818+
item: '_ScriptData' = {
819+
"name": os.path.basename(path),
820+
"script": content,
821+
"filename": path,
822+
}
823+
params = {
824+
"__version__": OWPythonScript.settings_version,
825+
"scriptLibrary": [
826+
item,
827+
],
828+
"scriptText": content
829+
}
830+
return params
831+
832+
803833
if __name__ == "__main__": # pragma: no cover
804834
WidgetPreview(OWPythonScript).run()

Orange/widgets/data/tests/test_owfile.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424

2525
from Orange.data.io import TabReader
2626
from Orange.tests import named_file
27-
from Orange.widgets.data.owfile import OWFile
27+
from Orange.widgets.data.owfile import OWFile, OWFileDropHandler
2828
from Orange.widgets.utils.filedialogs import dialog_formats, format_filter, RecentPath
2929
from Orange.widgets.tests.base import WidgetTest
3030
from Orange.widgets.utils.domaineditor import ComboDelegate, VarTypeDelegate, VarTableModel
@@ -666,5 +666,21 @@ def read():
666666
self.assertIn(WARNING_MSG, str(self.widget.Warning.load_warning))
667667

668668

669+
class TestOWFileDropHandler(unittest.TestCase):
670+
def test_canDropUrl(self):
671+
handler = OWFileDropHandler()
672+
self.assertTrue(handler.canDropUrl(QUrl("https://example.com/test.tab")))
673+
self.assertTrue(handler.canDropUrl(QUrl.fromLocalFile("test.tab")))
674+
675+
def test_parametersFromUrl(self):
676+
handler = OWFileDropHandler()
677+
r = handler.parametersFromUrl(QUrl("https://example.com/test.tab"))
678+
self.assertEqual(r["source"], OWFile.URL)
679+
self.assertEqual(r["recent_urls"], ["https://example.com/test.tab"])
680+
r = handler.parametersFromUrl(QUrl.fromLocalFile("test.tab"))
681+
self.assertEqual(r["source"], OWFile.LOCAL_FILE)
682+
self.assertEqual(r["recent_paths"][0].basename, "test.tab")
683+
684+
669685
if __name__ == "__main__":
670686
unittest.main()

Orange/widgets/data/tests/test_owpythonscript.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
# Test methods with long descriptive names can omit docstrings
22
# pylint: disable=missing-docstring
33
import sys
4+
import unittest
45

56
from AnyQt.QtCore import QMimeData, QUrl, QPoint, Qt
67
from AnyQt.QtGui import QDragEnterEvent, QDropEvent
78

89
from Orange.data import Table
910
from Orange.classification import LogisticRegressionLearner
1011
from Orange.tests import named_file
11-
from Orange.widgets.data.owpythonscript import OWPythonScript, read_file_content, Script
12+
from Orange.widgets.data.owpythonscript import OWPythonScript, \
13+
read_file_content, Script, OWPythonScriptDropHandler
1214
from Orange.widgets.tests.base import WidgetTest, DummySignalManager
1315
from Orange.widgets.widget import OWWidget
1416

@@ -260,3 +262,16 @@ def test_restore(self):
260262
"__version__": 2
261263
})
262264
self.assertEqual(w.libraryListSource[0].name, "A")
265+
266+
267+
class TestOWPythonScriptDropHandler(unittest.TestCase):
268+
def test_canDropFile(self):
269+
handler = OWPythonScriptDropHandler()
270+
self.assertTrue(handler.canDropFile(__file__))
271+
self.assertFalse(handler.canDropFile("test.tab"))
272+
273+
def test_parametersFromFile(self):
274+
handler = OWPythonScriptDropHandler()
275+
r = handler.parametersFromFile(__file__)
276+
item = r["scriptLibrary"][0]
277+
self.assertEqual(item["filename"], __file__)

Orange/widgets/model/owloadmodel.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
import os
22
import pickle
3+
from typing import Any, Dict
34

45
from AnyQt.QtWidgets import QSizePolicy, QStyle, QFileDialog
56
from AnyQt.QtCore import QTimer
67

8+
from orangewidget.workflow.drophandler import SingleFileDropHandler
9+
710
from Orange.base import Model
811
from Orange.widgets import widget, gui
912
from Orange.widgets.model import owsavemodel
10-
from Orange.widgets.utils.filedialogs import RecentPathsWComboMixin
13+
from Orange.widgets.utils.filedialogs import RecentPathsWComboMixin, RecentPath
1114
from Orange.widgets.utils import stdpaths
1215
from Orange.widgets.utils.widgetpreview import WidgetPreview
1316
from Orange.widgets.widget import Msg, Output
@@ -88,5 +91,18 @@ def open_file(self):
8891
self.Outputs.model.send(model)
8992

9093

94+
class OWLoadModelDropHandler(SingleFileDropHandler):
95+
WIDGET = OWLoadModel
96+
97+
def canDropFile(self, path: str) -> bool:
98+
return path.endswith(".pkcls")
99+
100+
def parametersFromFile(self, path: str) -> Dict[str, Any]:
101+
r = RecentPath(os.path.abspath(path), None, None,
102+
os.path.basename(path))
103+
parameters = {"recent_paths": [r]}
104+
return parameters
105+
106+
91107
if __name__ == "__main__": # pragma: no cover
92108
WidgetPreview(OWLoadModel).run()

Orange/widgets/model/tests/test_owloadmodel.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from orangewidget.utils.filedialogs import RecentPath
1212
from Orange.data import Table
1313
from Orange.classification.naive_bayes import NaiveBayesLearner
14-
from Orange.widgets.model.owloadmodel import OWLoadModel
14+
from Orange.widgets.model.owloadmodel import OWLoadModel, OWLoadModelDropHandler
1515
from Orange.widgets.tests.base import WidgetTest
1616

1717

@@ -136,5 +136,17 @@ def test_open_moved_workflow(self, load):
136136
os.remove(file_name)
137137

138138

139+
class TestOWLoadModelDropHandler(unittest.TestCase):
140+
def test_canDropFile(self):
141+
handler = OWLoadModelDropHandler()
142+
self.assertTrue(handler.canDropFile("test.pkcls"))
143+
self.assertFalse(handler.canDropFile("test.txt"))
144+
145+
def test_parametersFromFile(self):
146+
handler = OWLoadModelDropHandler()
147+
res = handler.parametersFromFile("test.pkcls")
148+
self.assertEqual(res["recent_paths"][0].basename, "test.pkcls")
149+
150+
139151
if __name__ == "__main__":
140152
unittest.main()

Orange/widgets/unsupervised/owdistancefile.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
33
from AnyQt.QtWidgets import QSizePolicy, QStyle, QMessageBox, QFileDialog
44
from AnyQt.QtCore import QTimer
55

6+
from orangewidget.workflow.drophandler import SingleFileDropHandler
7+
68
from Orange.misc import DistMatrix
79
from Orange.widgets import widget, gui
810
from Orange.data import get_sample_datasets_dir
9-
from Orange.widgets.utils.filedialogs import RecentPathsWComboMixin
11+
from Orange.widgets.utils.filedialogs import RecentPathsWComboMixin, RecentPath
1012
from Orange.widgets.utils.widgetpreview import WidgetPreview
1113
from Orange.widgets.widget import Output
1214

@@ -144,5 +146,17 @@ def send_report(self):
144146
self.report_items([("File name", self.loaded_file)])
145147

146148

149+
class OWDistanceFileDropHandler(SingleFileDropHandler):
150+
WIDGET = OWDistanceFile
151+
152+
def parametersFromFile(self, path):
153+
r = RecentPath(os.path.abspath(path), None, None,
154+
os.path.basename(path))
155+
return {"recent_paths": [r]}
156+
157+
def canDropFile(self, path: str) -> bool:
158+
return os.path.splitext(path)[1].lower() == ".dst"
159+
160+
147161
if __name__ == "__main__": # pragma: no cover
148162
WidgetPreview(OWDistanceFile).run()
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import unittest
2+
3+
from Orange.widgets.unsupervised.owdistancefile import OWDistanceFileDropHandler
4+
5+
6+
class TestOWDistanceFileDropHandler(unittest.TestCase):
7+
def test_canDropFile(self):
8+
handler = OWDistanceFileDropHandler()
9+
self.assertTrue(handler.canDropFile("test.dst"))
10+
self.assertFalse(handler.canDropFile("test.bin"))
11+
12+
def test_parametersFromFile(self):
13+
handler = OWDistanceFileDropHandler()
14+
r = handler.parametersFromFile("test.dst")
15+
self.assertEqual(r["recent_paths"][0].basename, "test.dst")

setup.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,12 @@
9797
"orange.canvas.help": (
9898
"html-index = Orange.widgets:WIDGET_HELP_PATH",
9999
),
100+
"orangecanvas.document.interactions.DropHandler": (
101+
"File = Orange.widgets.data.owfile:OWFileDropHandler",
102+
"Load Model = Orange.widgets.model.owloadmodel:OWLoadModelDropHandler",
103+
"Distance File = Orange.widgets.unsupervised.owdistancefile:OWDistanceFileDropHandler",
104+
"Python Script = Orange.widgets.data.owpythonscript:OWPythonScriptDropHandler",
105+
),
100106
"gui_scripts": (
101107
"orange-canvas = Orange.canvas.__main__:main",
102108
),

0 commit comments

Comments
 (0)