use native select folder dialog
This commit is contained in:
parent
1d52413618
commit
b2368b9053
6 changed files with 94 additions and 46 deletions
|
@ -35,6 +35,8 @@ def selectFolder(data):
|
||||||
cmd = ['./ctl', 'ui', 'folder']
|
cmd = ['./ctl', 'ui', 'folder']
|
||||||
if sys.platform == 'win32':
|
if sys.platform == 'win32':
|
||||||
cmd = win32_ui('folder')
|
cmd = win32_ui('folder')
|
||||||
|
if 'base' in data:
|
||||||
|
cmd += [data['base']]
|
||||||
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, close_fds=True)
|
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, close_fds=True)
|
||||||
stdout, stderr = p.communicate()
|
stdout, stderr = p.communicate()
|
||||||
path = stdout.decode('utf-8').strip()
|
path = stdout.decode('utf-8').strip()
|
||||||
|
@ -53,6 +55,8 @@ def selectFile(data):
|
||||||
cmd = ['./ctl', 'ui', 'file']
|
cmd = ['./ctl', 'ui', 'file']
|
||||||
if sys.platform == 'win32':
|
if sys.platform == 'win32':
|
||||||
cmd = win32_ui('file')
|
cmd = win32_ui('file')
|
||||||
|
if 'base' in data:
|
||||||
|
cmd += [data['base']]
|
||||||
p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
|
p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
|
||||||
stdout, stderr = p.communicate()
|
stdout, stderr = p.communicate()
|
||||||
path = stdout.decode('utf-8').strip()
|
path = stdout.decode('utf-8').strip()
|
||||||
|
|
46
oml/ui.py
46
oml/ui.py
|
@ -3,8 +3,9 @@
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
try:
|
try:
|
||||||
|
import gi
|
||||||
|
gi.require_version('Gtk', '3.0')
|
||||||
from gi.repository import Gtk, GObject
|
from gi.repository import Gtk, GObject
|
||||||
GObject.threads_init()
|
|
||||||
use_Gtk = True
|
use_Gtk = True
|
||||||
except:
|
except:
|
||||||
from tkinter import Tk, PhotoImage
|
from tkinter import Tk, PhotoImage
|
||||||
|
@ -13,13 +14,19 @@ except:
|
||||||
|
|
||||||
DEBUG = False
|
DEBUG = False
|
||||||
|
|
||||||
|
|
||||||
|
def short_home(path):
|
||||||
|
home = os.path.expanduser('~')
|
||||||
|
if path and path.startswith(home):
|
||||||
|
path = path.replace(home, '~')
|
||||||
|
return path
|
||||||
|
|
||||||
class GtkUI:
|
class GtkUI:
|
||||||
def selectFolder(self, data):
|
def selectFolder(self, data):
|
||||||
dialog = Gtk.FileChooserDialog(data.get("title", "Select Folder"),
|
dialog = Gtk.FileChooserDialog(title=data.get("title", "Select Folder"),
|
||||||
None,
|
action=Gtk.FileChooserAction.SELECT_FOLDER)
|
||||||
Gtk.FileChooserAction.SELECT_FOLDER,
|
dialog.add_buttons(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
|
||||||
(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
|
Gtk.STOCK_OPEN, Gtk.ResponseType.OK)
|
||||||
Gtk.STOCK_OPEN, Gtk.ResponseType.OK))
|
|
||||||
dialog.set_default_response(Gtk.ResponseType.OK)
|
dialog.set_default_response(Gtk.ResponseType.OK)
|
||||||
|
|
||||||
response = dialog.run()
|
response = dialog.run()
|
||||||
|
@ -36,14 +43,13 @@ class GtkUI:
|
||||||
Gtk.main_iteration()
|
Gtk.main_iteration()
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
print("done")
|
print("done")
|
||||||
return filename
|
return short_home(filename)
|
||||||
|
|
||||||
def selectFile(self, data):
|
def selectFile(self, data):
|
||||||
dialog = Gtk.FileChooserDialog(data.get("title", "Select File"),
|
dialog = Gtk.FileChooserDialog(title=data.get("title", "Select File"),
|
||||||
None,
|
action=Gtk.FileChooserAction.OPEN)
|
||||||
Gtk.FileChooserAction.OPEN,
|
dialog.add_buttons(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
|
||||||
(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
|
Gtk.STOCK_OPEN, Gtk.ResponseType.OK)
|
||||||
Gtk.STOCK_OPEN, Gtk.ResponseType.OK))
|
|
||||||
dialog.set_default_response(Gtk.ResponseType.OK)
|
dialog.set_default_response(Gtk.ResponseType.OK)
|
||||||
|
|
||||||
response = dialog.run()
|
response = dialog.run()
|
||||||
|
@ -60,7 +66,7 @@ class GtkUI:
|
||||||
Gtk.main_iteration()
|
Gtk.main_iteration()
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
print("done")
|
print("done")
|
||||||
return filename
|
return short_home(filename)
|
||||||
|
|
||||||
|
|
||||||
class TkUI:
|
class TkUI:
|
||||||
|
@ -77,10 +83,12 @@ class TkUI:
|
||||||
self.root.after_idle(self.root.call, 'wm', 'attributes', '.', '-topmost', False)
|
self.root.after_idle(self.root.call, 'wm', 'attributes', '.', '-topmost', False)
|
||||||
|
|
||||||
def selectFolder(self, data):
|
def selectFolder(self, data):
|
||||||
return tkinter.filedialog.askdirectory(parent=self.root, title=data.get("title", "Select Folder"))
|
folder = tkinter.filedialog.askdirectory(parent=self.root, title=data.get("title", "Select Folder"))
|
||||||
|
return short_home(folder)
|
||||||
|
|
||||||
def selectFile(self, data):
|
def selectFile(self, data):
|
||||||
return tkinter.filedialog.askopenfilename(parent=self.root, title=data.get("title", "Select File"))
|
filename = tkinter.filedialog.askopenfilename(parent=self.root, title=data.get("title", "Select File"))
|
||||||
|
return short_home(filename)
|
||||||
|
|
||||||
|
|
||||||
if use_Gtk:
|
if use_Gtk:
|
||||||
|
@ -89,8 +97,12 @@ else:
|
||||||
ui = TkUI()
|
ui = TkUI()
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
import sys
|
base = '~'
|
||||||
if len(sys.argv) == 2 and sys.argv[1] == 'folder':
|
if len(sys.argv) >= 3:
|
||||||
|
base = sys.argv[2]
|
||||||
|
base = os.path.expanduser(base)
|
||||||
|
os.chdir(base)
|
||||||
|
if len(sys.argv) >= 2 and sys.argv[1] == 'folder':
|
||||||
print(ui.selectFolder({}))
|
print(ui.selectFolder({}))
|
||||||
else:
|
else:
|
||||||
print(ui.selectFile({}))
|
print(ui.selectFile({}))
|
||||||
|
|
|
@ -217,14 +217,7 @@ oml.ui.importExportDialog = function() {
|
||||||
var $element = Ox.Element(),
|
var $element = Ox.Element(),
|
||||||
$form = Ox.Form({
|
$form = Ox.Form({
|
||||||
items: selected == 'import' ? [
|
items: selected == 'import' ? [
|
||||||
Ox.Input({
|
oml.ui.selectFolder({
|
||||||
autocomplete: function(value, callback) {
|
|
||||||
oml.api.autocompleteFolder({path: value}, function(result) {
|
|
||||||
callback(result.data.items);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
autocompleteSelect: true,
|
|
||||||
changeOnKeypress: true,
|
|
||||||
id: 'path',
|
id: 'path',
|
||||||
label: 'Source Path',
|
label: 'Source Path',
|
||||||
labelWidth: 128,
|
labelWidth: 128,
|
||||||
|
@ -256,14 +249,7 @@ oml.ui.importExportDialog = function() {
|
||||||
width: 480
|
width: 480
|
||||||
})
|
})
|
||||||
] : [
|
] : [
|
||||||
Ox.Input({
|
oml.ui.selectFolder({
|
||||||
autocomplete: function(value, callback) {
|
|
||||||
oml.api.autocompleteFolder({path: value}, function(result) {
|
|
||||||
callback(result.data.items);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
autocompleteSelect: true,
|
|
||||||
changeOnKeypress: true,
|
|
||||||
id: 'path',
|
id: 'path',
|
||||||
label: 'Destination Path',
|
label: 'Destination Path',
|
||||||
labelWidth: 128,
|
labelWidth: 128,
|
||||||
|
@ -296,6 +282,7 @@ oml.ui.importExportDialog = function() {
|
||||||
.bindEvent({
|
.bindEvent({
|
||||||
change: function(data) {
|
change: function(data) {
|
||||||
var values = $form.values();
|
var values = $form.values();
|
||||||
|
console.log(values)
|
||||||
$activityButton[selected].options({
|
$activityButton[selected].options({
|
||||||
disabled: !values.path
|
disabled: !values.path
|
||||||
});
|
});
|
||||||
|
|
|
@ -25,24 +25,14 @@ oml.ui.preferencesPanel = function() {
|
||||||
{
|
{
|
||||||
id: 'libraryPath',
|
id: 'libraryPath',
|
||||||
title: 'Library Path',
|
title: 'Library Path',
|
||||||
autocomplete: function(value, callback) {
|
type: 'selectfolder',
|
||||||
oml.api.autocompleteFolder({path: value}, function(result) {
|
|
||||||
callback(result.data.items);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
autocompleteSelect: true,
|
|
||||||
value: preferences.libraryPath,
|
value: preferences.libraryPath,
|
||||||
help: 'The directory in which your "Books" folder is located. This is where your media files are stored. It can be on your local machine, but just as well on an external drive or networked volume.'
|
help: 'The directory in which your "Books" folder is located. This is where your media files are stored. It can be on your local machine, but just as well on an external drive or networked volume.'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'importPath',
|
id: 'importPath',
|
||||||
title: 'Import Path',
|
title: 'Import Path',
|
||||||
autocomplete: function(value, callback) {
|
type: 'selectfolder',
|
||||||
oml.api.autocompleteFolder({path: value}, function(result) {
|
|
||||||
callback(result.data.items);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
autocompleteSelect: true,
|
|
||||||
value: preferences.importPath,
|
value: preferences.importPath,
|
||||||
help: 'Any media files that you put in this folder will be added to your library. Once added, they will be removed from this folder.'
|
help: 'Any media files that you put in this folder will be added to your library. Once added, they will be removed from this folder.'
|
||||||
}
|
}
|
||||||
|
@ -347,6 +337,15 @@ oml.ui.preferencesPanel = function() {
|
||||||
value: item.value,
|
value: item.value,
|
||||||
width: 384
|
width: 384
|
||||||
})
|
})
|
||||||
|
: item.type == 'selectfolder'
|
||||||
|
? oml.ui.selectFolder({
|
||||||
|
label: Ox._(item.title),
|
||||||
|
labelWidth: 128,
|
||||||
|
placeholder: item.placeholder || '',
|
||||||
|
style: 'squared',
|
||||||
|
title: oml.user.preferences[item.id] || item.value || '',
|
||||||
|
width: 384 - !!item.unit * 48
|
||||||
|
})
|
||||||
: Ox.Input({
|
: Ox.Input({
|
||||||
autocomplete: item.autocomplete || null,
|
autocomplete: item.autocomplete || null,
|
||||||
autocompleteSelect: item.autocompleteSelect || false,
|
autocompleteSelect: item.autocompleteSelect || false,
|
||||||
|
@ -357,7 +356,6 @@ oml.ui.preferencesPanel = function() {
|
||||||
value: oml.user.preferences[item.id] || item.value || '',
|
value: oml.user.preferences[item.id] || item.value || '',
|
||||||
width: 384 - !!item.unit * 48
|
width: 384 - !!item.unit * 48
|
||||||
})
|
})
|
||||||
|
|
||||||
].concat(item.unit ? [
|
].concat(item.unit ? [
|
||||||
Ox.Label({
|
Ox.Label({
|
||||||
overlap: 'left',
|
overlap: 'left',
|
||||||
|
|
46
static/js/selectFolder.js
Normal file
46
static/js/selectFolder.js
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
oml.ui.selectFolder = function(options, self) {
|
||||||
|
options.width = options.width - options.labelWidth
|
||||||
|
options = Ox.extend({
|
||||||
|
title: '...',
|
||||||
|
textAlign: 'left'
|
||||||
|
}, options);
|
||||||
|
var $button = Ox.Button(options, self).bindEvent({
|
||||||
|
click: function(event) {
|
||||||
|
oml.api.selectFolder({
|
||||||
|
base: $button.value()
|
||||||
|
}, function(result) {
|
||||||
|
if (result.data.path) {
|
||||||
|
$button.value(result.data.path);
|
||||||
|
$button.options({
|
||||||
|
title: result.data.path
|
||||||
|
});
|
||||||
|
$button.triggerEvent('change', result.data.path);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
that = Ox.FormElementGroup({
|
||||||
|
id: options.id,
|
||||||
|
elements: [
|
||||||
|
Ox.Label({
|
||||||
|
style: options.style,
|
||||||
|
textAlign: 'right',
|
||||||
|
overlap: 'right',
|
||||||
|
title: options.label,
|
||||||
|
width: options.labelWidth
|
||||||
|
}),
|
||||||
|
$button
|
||||||
|
],
|
||||||
|
}, self);
|
||||||
|
|
||||||
|
that.value = function() {
|
||||||
|
return $button.value()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.title && options.title != '...') {
|
||||||
|
$button.value(options.title)
|
||||||
|
}
|
||||||
|
return that;
|
||||||
|
}
|
|
@ -58,6 +58,7 @@
|
||||||
"rightPanel.js",
|
"rightPanel.js",
|
||||||
"sectionButtons.js",
|
"sectionButtons.js",
|
||||||
"sectionbar.js",
|
"sectionbar.js",
|
||||||
|
"selectFolder.js",
|
||||||
"sortElement.js",
|
"sortElement.js",
|
||||||
"statusIcon.js",
|
"statusIcon.js",
|
||||||
"statusbar.js",
|
"statusbar.js",
|
||||||
|
|
Loading…
Reference in a new issue