├── .gitignore
├── README.md
├── elmextensions
├── SearchableList.py
├── StandardButton.py
├── StandardPopup.py
├── __init__.py
├── aboutwindow.py
├── easythreading.py
├── embeddedterminal.py
├── fileselector.py
├── sortedlist.py
└── tabbedbox.py
├── license.txt
├── setup.py
├── sortedlistother
├── sortedgenlist.py
├── sortedlist.py
└── test_sortedlist.py
├── test_aboutwindow.py
├── test_embeddedterminal.py
├── test_fileselector.py
├── test_searchablelist.py
├── test_sortedlist.py
└── test_tabbedbox.py
/.gitignore:
--------------------------------------------------------------------------------
1 | *.py[co]
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | A library that contains a few more complex elementary objects for easy importing/usage.
2 |
3 | Current Objects:
4 |
5 | - SortedList
6 |
7 | 
8 |
9 | - EmbeddedTerminal
10 |
11 | 
12 |
13 | - AboutWindow
14 |
15 | 
16 |
17 | - FileSelector
18 |
19 | 
20 |
21 | - TabbedBox
22 |
23 | 
24 |
25 | - SearchableList
26 |
27 | 
28 |
29 | Credits:
30 | - [Jeff Hoogland](http://www.jeffhoogland.com/)
31 | - [Kai Huuhko](https://github.com/kaihu)
32 | - [Wolfgang Morawetz](https://github.com/wfx/)
33 |
--------------------------------------------------------------------------------
/elmextensions/SearchableList.py:
--------------------------------------------------------------------------------
1 | try:
2 | import sys
3 | reload(sys)
4 | sys.setdefaultencoding('utf8')
5 | except NameError:
6 | pass
7 |
8 | from efl.elementary.box import Box
9 | from efl.elementary.frame import Frame
10 | from efl.elementary.button import Button
11 | from efl.elementary.entry import Entry
12 | from efl.elementary.list import List
13 | from efl.evas import EVAS_HINT_EXPAND, EVAS_HINT_FILL
14 |
15 | EXPAND_BOTH = EVAS_HINT_EXPAND, EVAS_HINT_EXPAND
16 | EXPAND_HORIZ = EVAS_HINT_EXPAND, 0.0
17 | FILL_BOTH = EVAS_HINT_FILL, EVAS_HINT_FILL
18 | FILL_HORIZ = EVAS_HINT_FILL, 0.5
19 | ALIGN_CENTER = 0.5, 0.5
20 |
21 | def searchList(text, lst):
22 | for item in lst:
23 | if text.lower() in item.lower()[:len(text)]:
24 | return lst.index(item)
25 | return 0
26 |
27 | class SearchableList(Box):
28 | def __init__(self, parent_widget, *args, **kwargs):
29 | Box.__init__(self, parent_widget, *args, **kwargs)
30 |
31 | self.ourList = ourList = List(self, size_hint_weight=EXPAND_BOTH, size_hint_align=FILL_BOTH)
32 |
33 | self.keys = []
34 |
35 | ourList.go()
36 | ourList.show()
37 |
38 | self.ourItems = []
39 |
40 | sframe = Frame(self, size_hint_weight=EXPAND_HORIZ, size_hint_align=FILL_HORIZ)
41 | sframe.text = "Search"
42 | self.search = search = Entry(self)
43 | search.single_line = True
44 | search.callback_changed_add(self.searchChange)
45 | sframe.content = search
46 | search.show()
47 | sframe.show()
48 |
49 | self.pack_end(ourList)
50 | self.pack_end(sframe)
51 |
52 | def callback_item_focused_add( self, ourCB ):
53 | self.ourList.callback_item_focused_add( ourCB )
54 |
55 | def callback_clicked_double_add( self, ourCB ):
56 | self.ourList.callback_clicked_double_add( ourCB )
57 |
58 | def item_append( self, text, ourIcon=None ):
59 | self.keys.append(text)
60 | self.keys.sort()
61 |
62 | itemSpot = self.keys.index(text)
63 |
64 | if not len(self.ourItems) or itemSpot > len(self.ourItems)-1:
65 | item = self.ourList.item_append(text, icon=ourIcon)
66 | self.ourItems.append(item)
67 | else:
68 | #print("Inserting after item %s"%self.ourItems[itemSpot])
69 | item = self.ourList.item_insert_before(self.ourItems[itemSpot], text, icon=ourIcon)
70 | self.ourItems.insert(itemSpot, item)
71 |
72 | return item
73 |
74 | def items_get( self ):
75 | return self.ourList.items_get()
76 |
77 | def selected_item_get( self ):
78 | return self.ourList.selected_item_get()
79 |
80 | def searchChange( self, entry ):
81 | #print entry.text
82 | zeindex = searchList(entry.text, self.keys)
83 | self.ourItems[zeindex].selected_set(True)
84 | self.ourItems[zeindex].bring_in()
85 | self.search.focus = True
86 |
--------------------------------------------------------------------------------
/elmextensions/StandardButton.py:
--------------------------------------------------------------------------------
1 | from efl.evas import EVAS_HINT_EXPAND, EVAS_HINT_FILL
2 | from efl import elementary
3 | from efl.elementary.button import Button
4 | from efl.elementary.box import Box
5 | from efl.elementary.icon import Icon
6 |
7 | EXPAND_BOTH = EVAS_HINT_EXPAND, EVAS_HINT_EXPAND
8 | EXPAND_HORIZ = EVAS_HINT_EXPAND, 0.0
9 | FILL_BOTH = EVAS_HINT_FILL, EVAS_HINT_FILL
10 |
11 | class StandardButton(Button):
12 | def __init__(self, ourParent, ourText, ourIcon=None, ourCB=None, *args, **kwargs):
13 | Button.__init__(self, ourParent, *args, **kwargs)
14 | icon = Icon(self, size_hint_weight=EXPAND_BOTH, size_hint_align=FILL_BOTH)
15 | icon.standard_set(ourIcon)
16 | icon.show()
17 |
18 | self.text = ourText
19 | self.content_set(icon)
20 | self.callback_clicked_add(ourCB)
21 |
--------------------------------------------------------------------------------
/elmextensions/StandardPopup.py:
--------------------------------------------------------------------------------
1 | #Borrowed from ePad error popup done by ylee
2 |
3 | from efl.evas import EVAS_HINT_EXPAND, EVAS_HINT_FILL
4 | from efl import elementary
5 | from efl.elementary.box import Box
6 | from efl.elementary.icon import Icon
7 | from efl.elementary.button import Button
8 | from efl.elementary.image import Image
9 | from efl.elementary.popup import Popup
10 | from efl.elementary.label import Label, ELM_WRAP_WORD
11 | from efl.elementary.table import Table
12 | from efl.elementary.need import need_ethumb
13 |
14 | EXPAND_BOTH = EVAS_HINT_EXPAND, EVAS_HINT_EXPAND
15 | EXPAND_HORIZ = EVAS_HINT_EXPAND, 0.0
16 | FILL_BOTH = EVAS_HINT_FILL, EVAS_HINT_FILL
17 | FILL_HORIZ = EVAS_HINT_FILL, 0.5
18 | ALIGN_CENTER = 0.5, 0.5
19 |
20 | class StandardPopup(Popup):
21 | def __init__(self, ourParent, ourMsg, ourIcon=None, *args, **kwargs):
22 | Popup.__init__(self, ourParent, *args, **kwargs)
23 | self.callback_block_clicked_add(lambda obj: self.delete())
24 |
25 | # Add a table to hold dialog image and text to Popup
26 | tb = Table(self, size_hint_weight=EXPAND_BOTH)
27 | self.part_content_set("default", tb)
28 | tb.show()
29 |
30 | # Add dialog-error Image to table
31 | need_ethumb()
32 | icon = Icon(self, thumb='True')
33 | icon.standard_set(ourIcon)
34 | # Using gksudo or sudo fails to load Image here
35 | # unless options specify using preserving their existing environment.
36 | # may also fail to load other icons but does not raise an exception
37 | # in that situation.
38 | # Works fine using eSudo as a gksudo alternative,
39 | # other alternatives not tested
40 | try:
41 | dialogImage = Image(self,
42 | size_hint_weight=EXPAND_HORIZ,
43 | size_hint_align=FILL_BOTH,
44 | file=icon.file_get())
45 | tb.pack(dialogImage, 0, 0, 1, 1)
46 | dialogImage.show()
47 | except RuntimeError:
48 | # An error message is displayed for this same error
49 | # when aboutWin is initialized so no need to redisplay.
50 | pass
51 | # Add dialog text to table
52 | dialogLabel = Label(self, line_wrap=ELM_WRAP_WORD,
53 | size_hint_weight=EXPAND_HORIZ,
54 | size_hint_align=FILL_BOTH)
55 | dialogLabel.text = ourMsg
56 | tb.pack(dialogLabel, 1, 0, 1, 1)
57 | dialogLabel.show()
58 |
59 | # Ok Button
60 | ok_btt = Button(self)
61 | ok_btt.text = "Ok"
62 | ok_btt.callback_clicked_add(lambda obj: self.delete())
63 | ok_btt.show()
64 |
65 | # add button to popup
66 | self.part_content_set("button3", ok_btt)
67 |
--------------------------------------------------------------------------------
/elmextensions/__init__.py:
--------------------------------------------------------------------------------
1 | from .sortedlist import *
2 | from .embeddedterminal import *
3 | from .aboutwindow import *
4 | from .fileselector import *
5 | from .tabbedbox import *
6 | from .StandardButton import *
7 | from .StandardPopup import *
8 | from .SearchableList import *
9 |
10 | __copyright__ = "Copyright 2015-2017 Jeff Hoogland"
11 | __license__ = "BSD-3-clause"
12 |
13 | # the version number: major, minor, micro, releaselevel, and serial.
14 | __version__ = "0.2.1rc.2"
15 | version_string = __version__
16 |
--------------------------------------------------------------------------------
/elmextensions/aboutwindow.py:
--------------------------------------------------------------------------------
1 | from efl.ecore import Exe
2 | from efl.evas import EVAS_HINT_EXPAND, EVAS_HINT_FILL
3 | from efl import elementary
4 | from efl.elementary.window import Window, ELM_WIN_DIALOG_BASIC
5 | from efl.elementary.background import Background
6 | from efl.elementary.box import Box
7 | from efl.elementary.button import Button
8 | from efl.elementary.label import Label, ELM_WRAP_WORD
9 | from efl.elementary.icon import Icon
10 | from efl.elementary.separator import Separator
11 | from efl.elementary.frame import Frame
12 | from efl.elementary.entry import Entry, ELM_TEXT_FORMAT_PLAIN_UTF8, \
13 | ELM_WRAP_NONE, ELM_WRAP_MIXED
14 | from efl.evas import EVAS_HINT_EXPAND, EVAS_HINT_FILL
15 |
16 | EXPAND_BOTH = EVAS_HINT_EXPAND, EVAS_HINT_EXPAND
17 | EXPAND_HORIZ = EVAS_HINT_EXPAND, 0.0
18 | EXPAND_VERT = 0.0, EVAS_HINT_EXPAND
19 | FILL_BOTH = EVAS_HINT_FILL, EVAS_HINT_FILL
20 | FILL_HORIZ = EVAS_HINT_FILL, 0.5
21 | FILL_VERT = 0.5, EVAS_HINT_FILL
22 |
23 | def xdg_open(url_or_file):
24 | Exe('xdg-open "%s"' % url_or_file)
25 |
26 | class InstanceError(Exception):
27 | pass
28 |
29 | class AboutWindow(Window):
30 | __initialized = False
31 |
32 | def __init__(self, parent, title="About", standardicon="dialog-information", \
33 | version="N/A", authors="No One", \
34 | licen="GPL", webaddress="", info="Something, something, turtles"):
35 |
36 | if AboutWindow.__initialized:
37 | raise InstanceError("You can't create more than 1 instance of AboutWindow")
38 | AboutWindow.__initialized = True
39 |
40 | Window.__init__(self, title, ELM_WIN_DIALOG_BASIC, autodel=True)
41 | self.callback_delete_request_add(self.close_inst)
42 | background = Background(self, size_hint_weight=EXPAND_BOTH)
43 | self.resize_object_add(background)
44 | background.show()
45 |
46 | fr = Frame(self, style='pad_large', size_hint_weight=EXPAND_BOTH,
47 | size_hint_align=FILL_BOTH)
48 | self.resize_object_add(fr)
49 | fr.show()
50 |
51 | hbox = Box(self, horizontal=True, padding=(12,12))
52 | fr.content = hbox
53 | hbox.show()
54 |
55 | vbox = Box(self, align=(0.0,0.0), padding=(6,6),
56 | size_hint_weight=EXPAND_VERT, size_hint_align=FILL_VERT)
57 | hbox.pack_end(vbox)
58 | vbox.show()
59 |
60 | # icon + version
61 | ic = Icon(self, size_hint_min=(64,64))
62 | ic.standard_set(standardicon)
63 | vbox.pack_end(ic)
64 | ic.show()
65 |
66 | lb = Label(self, text=('Version: %s') % version)
67 | vbox.pack_end(lb)
68 | lb.show()
69 |
70 | sep = Separator(self, horizontal=True)
71 | vbox.pack_end(sep)
72 | sep.show()
73 |
74 | # buttons
75 | bt = Button(self, text=(title), size_hint_align=FILL_HORIZ)
76 | bt.callback_clicked_add(lambda b: self.entry.text_set(info))
77 | vbox.pack_end(bt)
78 | bt.show()
79 |
80 | bt = Button(self, text=('Website'),size_hint_align=FILL_HORIZ)
81 | bt.callback_clicked_add(lambda b: xdg_open(webaddress))
82 | vbox.pack_end(bt)
83 | bt.show()
84 |
85 | bt = Button(self, text=('Authors'), size_hint_align=FILL_HORIZ)
86 | bt.callback_clicked_add(lambda b: self.entry.text_set(authors))
87 | vbox.pack_end(bt)
88 | bt.show()
89 |
90 | bt = Button(self, text=('License'), size_hint_align=FILL_HORIZ)
91 | bt.callback_clicked_add(lambda b: self.entry.text_set(licen))
92 | vbox.pack_end(bt)
93 | bt.show()
94 |
95 | # main text
96 | self.entry = Entry(self, editable=False, scrollable=True, text=info,
97 | size_hint_weight=EXPAND_BOTH, size_hint_align=FILL_BOTH)
98 | self.entry.callback_anchor_clicked_add(lambda e,i: xdg_open(i.name))
99 | hbox.pack_end(self.entry)
100 | self.entry.show()
101 |
102 | self.resize(400, 200)
103 | self.show()
104 |
105 | def close_inst(self, obj):
106 | AboutWindow.__initialized = False
107 |
--------------------------------------------------------------------------------
/elmextensions/easythreading.py:
--------------------------------------------------------------------------------
1 | from efl import ecore
2 |
3 | import threading
4 | try:
5 | import Queue
6 | except:
7 | import queue as Queue
8 |
9 | class ThreadedFunction(object):
10 | def __init__(self, doneCB=None):
11 | # private stuff
12 | self._commandQueue = Queue.Queue()
13 | self._replyQueue = Queue.Queue()
14 | self._doneCB = doneCB
15 |
16 | # add a timer to check the data returned by the worker thread
17 | self._timer = ecore.Timer(0.1, self.checkReplyQueue)
18 |
19 | # start the working thread
20 | threading.Thread(target=self.threadFunc).start()
21 |
22 | def run(self, action):
23 | self._commandQueue.put(action)
24 |
25 | def shutdown(self):
26 | self._timer.delete()
27 | self._commandQueue.put('QUIT')
28 |
29 | def checkReplyQueue(self):
30 | if not self._replyQueue.empty():
31 | result = self._replyQueue.get()
32 | if callable(self._doneCB):
33 | self._doneCB()
34 | return True
35 |
36 | # all the member below this point run in the thread
37 | def threadFunc(self):
38 | while True:
39 | # wait here until an item in the queue is present
40 | func = self._commandQueue.get()
41 | if callable(func):
42 | func()
43 | elif func == 'QUIT':
44 | break
45 | self._replyQueue.put("done")
46 |
--------------------------------------------------------------------------------
/elmextensions/embeddedterminal.py:
--------------------------------------------------------------------------------
1 | from efl import ecore
2 | from efl.elementary.box import Box
3 | from efl.elementary.frame import Frame
4 | from efl.elementary.button import Button
5 | from efl.elementary.entry import Entry, markup_to_utf8
6 | from efl.evas import EVAS_HINT_EXPAND, EVAS_HINT_FILL
7 |
8 | EXPAND_BOTH = EVAS_HINT_EXPAND, EVAS_HINT_EXPAND
9 | EXPAND_HORIZ = EVAS_HINT_EXPAND, 0.0
10 | FILL_BOTH = EVAS_HINT_FILL, EVAS_HINT_FILL
11 | FILL_HORIZ = EVAS_HINT_FILL, 0.5
12 |
13 | class EmbeddedTerminal(Box):
14 | def __init__(self, parent_widget, titles=None, *args, **kwargs):
15 | Box.__init__(self, parent_widget, *args, **kwargs)
16 |
17 | self.outPut = Entry(self, size_hint_weight=EXPAND_BOTH, size_hint_align=FILL_BOTH)
18 | self.outPut.editable_set(False)
19 | self.outPut.scrollable_set(True)
20 | self.outPut.callback_changed_add(self.changedCb)
21 | self.outPut.show()
22 |
23 | frame = Frame(self, size_hint_weight=EXPAND_HORIZ, size_hint_align=FILL_HORIZ)
24 | frame.text = "Input:"
25 | frame.autocollapse_set(True)
26 | frame.collapse_go(True)
27 | frame.show()
28 |
29 | bx = Box(self, size_hint_weight=EXPAND_HORIZ, size_hint_align=FILL_HORIZ)
30 | bx.horizontal = True
31 | bx.show()
32 |
33 | frame.content = bx
34 |
35 | self.inPut = Entry(self, size_hint_weight=EXPAND_BOTH, size_hint_align=FILL_BOTH)
36 | self.inPut.single_line_set(True)
37 | self.inPut.callback_activated_add(self.enterPressed)
38 | self.inPut.show()
39 |
40 | enterButton = Button(self)
41 | enterButton.text = "Execute"
42 | enterButton.callback_pressed_add(self.enterPressed)
43 | enterButton.show()
44 |
45 | bx.pack_end(self.inPut)
46 | bx.pack_end(enterButton)
47 |
48 | self.pack_end(self.outPut)
49 | self.pack_end(frame)
50 |
51 | self.cmd_exe = None
52 | self.done_cb = None
53 |
54 | def changedCb(self, obj):
55 | obj.cursor_end_set()
56 |
57 | def enterPressed(self, btn):
58 | if not self.cmd_exe:
59 | self.runCommand(self.inPut.text)
60 | self.inPut.text = ""
61 | else:
62 | ourResult = self.cmd_exe.send("%s\n"%self.inPut.text)
63 | self.inPut.text = ""
64 |
65 | def runCommand(self, command, done_cb=None):
66 | command = markup_to_utf8(command)
67 | self.cmd_exe = cmd = ecore.Exe(
68 | command,
69 | ecore.ECORE_EXE_PIPE_READ |
70 | ecore.ECORE_EXE_PIPE_ERROR |
71 | ecore.ECORE_EXE_PIPE_WRITE
72 | )
73 | cmd.on_add_event_add(self.command_started)
74 | cmd.on_data_event_add(self.received_data)
75 | cmd.on_error_event_add(self.received_error)
76 | cmd.on_del_event_add(self.command_done)
77 |
78 | self.done_cb = done_cb
79 |
80 | def command_started(self, cmd, event, *args, **kwargs):
81 | self.outPut.entry_append("---------------------------------")
82 | self.outPut.entry_append(" ")
83 |
84 | def received_data(self, cmd, event, *args, **kwargs):
85 | self.outPut.entry_append("%s"%event.data)
86 | self.outPut.entry_append(" ")
87 |
88 | def received_error(self, cmd, event, *args, **kwargs):
89 | self.outPut.entry_append("Error: %s" % event.data)
90 |
91 | def command_done(self, cmd, event, *args, **kwargs):
92 | self.outPut.entry_append("---------------------------------")
93 | self.outPut.entry_append(" ")
94 | self.cmd_exe = None
95 | if self.done_cb:
96 | if callable(self.done_cb):
97 | self.done_cb()
98 |
--------------------------------------------------------------------------------
/elmextensions/fileselector.py:
--------------------------------------------------------------------------------
1 | # encoding: utf-8
2 |
3 | from efl.elementary.label import Label
4 | from efl.elementary.icon import Icon
5 | from efl.elementary.box import Box
6 | from efl.elementary.list import List
7 | from efl.elementary.genlist import Genlist, GenlistItem, GenlistItemClass, \
8 | ELM_LIST_COMPRESS
9 | from efl.elementary.button import Button
10 | from efl.elementary.hoversel import Hoversel
11 | from efl.elementary.separator import Separator
12 | from efl.elementary.panes import Panes
13 | from efl.elementary.popup import Popup
14 | from efl.elementary.entry import Entry, ELM_INPUT_HINT_AUTO_COMPLETE
15 | from efl.elementary.image import Image
16 | from efl.evas import EVAS_HINT_EXPAND, EVAS_HINT_FILL, EVAS_CALLBACK_KEY_DOWN
17 | from efl import ecore
18 |
19 | #imported to work around a bug
20 | import efl.elementary.layout
21 |
22 | import os
23 | import math
24 | from .easythreading import ThreadedFunction
25 | from collections import deque
26 |
27 | EXPAND_BOTH = EVAS_HINT_EXPAND, EVAS_HINT_EXPAND
28 | EXPAND_HORIZ = EVAS_HINT_EXPAND, 0.0
29 | FILL_BOTH = EVAS_HINT_FILL, EVAS_HINT_FILL
30 | FILL_HORIZ = EVAS_HINT_FILL, 0.5
31 |
32 |
33 | class FileGLIC(GenlistItemClass):
34 |
35 | def text_get(self, gl, part, data):
36 | return data["d"]
37 |
38 | def content_get(self, gl, part, data):
39 | if part == "elm.swallow.icon":
40 | return Icon(
41 | gl,
42 | standard="gtk-file"
43 | )
44 |
45 | fileglic = FileGLIC(item_style="one_icon")
46 |
47 |
48 | class DirGLIC(GenlistItemClass):
49 |
50 | def text_get(self, gl, part, data):
51 | return data["d"]
52 |
53 | def content_get(self, gl, part, data):
54 | if part == "elm.swallow.icon":
55 | return Icon(
56 | gl,
57 | standard="gtk-directory"
58 | )
59 |
60 | dirglic = DirGLIC(item_style="one_icon")
61 |
62 |
63 | class FileSelector(Box):
64 | def __init__(self, parent_widget, defaultPath="", defaultPopulate=True, *args, **kwargs):
65 | Box.__init__(self, parent_widget, *args, **kwargs)
66 |
67 | self.cancelCallback = None
68 | self.actionCallback = None
69 | self.directoryChangeCallback = None
70 |
71 | self.threadedFunction = ThreadedFunction()
72 | self._timer = ecore.Timer(0.02, self.populateFile)
73 |
74 | #Watch key presses for ctrl+l to select entry
75 | parent_widget.elm_event_callback_add(self.eventsCb)
76 |
77 | self.selectedFolder = None
78 | self.showHidden = False
79 | self.currentDirectory = None
80 | self.focusedEntry = None
81 | self.folderOnly = False
82 | self.sortReverse = False
83 | self.addingHidden = False
84 | self.pendingFiles = deque()
85 | self.currentSubFolders = []
86 | self.currentFiles = []
87 |
88 | #Mode should be "save" or "load"
89 | self.mode = "save"
90 |
91 | self.home = os.path.expanduser("~")
92 | self.root = "/"
93 |
94 | #Label+Entry for File Name
95 | self.filenameBox = Box(self, size_hint_weight=EXPAND_HORIZ,
96 | size_hint_align=FILL_HORIZ)
97 | self.filenameBox.horizontal = True
98 | self.filenameBox.show()
99 |
100 | fileLabel = Label(self, size_hint_weight=(0.15, EVAS_HINT_EXPAND),
101 | size_hint_align=FILL_HORIZ)
102 | fileLabel.text = "Filename:"
103 | fileLabel.show()
104 |
105 | self.fileEntry = Entry(self, size_hint_weight=EXPAND_BOTH,
106 | size_hint_align=FILL_HORIZ)
107 | self.fileEntry.single_line_set(True)
108 | self.fileEntry.scrollable_set(True)
109 | self.fileEntry.callback_changed_user_add(self.fileEntryChanged)
110 | self.fileEntry.show()
111 |
112 | self.filenameBox.pack_end(fileLabel)
113 | self.filenameBox.pack_end(self.fileEntry)
114 |
115 | sep = Separator(self, size_hint_weight=EXPAND_HORIZ,
116 | size_hint_align=FILL_HORIZ)
117 | sep.horizontal_set(True)
118 | sep.show()
119 |
120 | #Label+Entry for File Path
121 | self.filepathBox = Box(self, size_hint_weight=EXPAND_HORIZ,
122 | size_hint_align=FILL_HORIZ)
123 | self.filepathBox.horizontal = True
124 | self.filepathBox.show()
125 |
126 | fileLabel = Label(self, size_hint_weight=(0.15, EVAS_HINT_EXPAND),
127 | size_hint_align=FILL_HORIZ)
128 | fileLabel.text = "Current Folder:"
129 | fileLabel.show()
130 |
131 | self.filepathEntry = Entry(self, size_hint_weight=EXPAND_BOTH,
132 | size_hint_align=FILL_HORIZ)
133 | self.filepathEntry.single_line_set(True)
134 | self.filepathEntry.scrollable_set(True)
135 | self.filepathEntry.callback_changed_user_add(self.fileEntryChanged)
136 | self.filepathEntry.callback_unfocused_add(self.filepathEditDone)
137 | self.filepathEntry.callback_activated_add(self.filepathEditDone)
138 | #Wish this worked. Doesn't seem to do anything
139 | #self.filepathEntry.input_hint_set(ELM_INPUT_HINT_AUTO_COMPLETE)
140 |
141 | if defaultPath and os.path.isdir(defaultPath):
142 | startPath = defaultPath
143 | else:
144 | startPath = self.home
145 | self.filepathEntry.show()
146 |
147 | self.filepathBox.pack_end(fileLabel)
148 | self.filepathBox.pack_end(self.filepathEntry)
149 |
150 | self.autocompleteHover = Hoversel(self, hover_parent=self)
151 | self.autocompleteHover.callback_selected_add(self.autocompleteSelected)
152 | #self.autocompleteHover.show()
153 |
154 | self.fileSelectorBox = Panes(self, content_left_size=0.3,
155 | size_hint_weight=EXPAND_BOTH, size_hint_align=FILL_BOTH)
156 | self.fileSelectorBox.show()
157 |
158 | """Bookmarks Box contains:
159 |
160 | - Button - Up Arrow
161 | - List - Home/Root/GTK bookmarks
162 | - Box
163 | -- Button - Add Bookmark
164 | -- Button - Remove Bookmark"""
165 | self.bookmarkBox = Box(self, size_hint_weight=(0.3, EVAS_HINT_EXPAND),
166 | size_hint_align=FILL_BOTH)
167 | self.bookmarkBox.show()
168 |
169 |
170 | upIcon = Icon(self, size_hint_weight=EXPAND_BOTH,
171 | size_hint_align=FILL_BOTH)
172 | upIcon.standard_set("go-up")
173 | upIcon.show()
174 |
175 | self.upButton = Button(self, size_hint_weight=EXPAND_HORIZ,
176 | size_hint_align=FILL_HORIZ, content=upIcon)
177 | self.upButton.text = "Up"
178 | self.upButton.callback_pressed_add(self.upButtonPressed)
179 | self.upButton.show()
180 |
181 | self.bookmarksList = List(self, size_hint_weight=EXPAND_BOTH,
182 | size_hint_align=FILL_BOTH)
183 | self.bookmarksList.callback_activated_add(self.bookmarkDoubleClicked)
184 | self.bookmarksList.show()
185 |
186 | self.bookmarkModBox = Box(self, size_hint_weight=EXPAND_HORIZ,
187 | size_hint_align=FILL_HORIZ)
188 | self.bookmarkModBox.horizontal = True
189 | self.bookmarkModBox.show()
190 |
191 | con = Icon(self, size_hint_weight=EXPAND_BOTH,
192 | size_hint_align=FILL_BOTH)
193 | con.standard_set("add")
194 | con.show()
195 |
196 | self.addButton = Button(self, size_hint_weight=EXPAND_HORIZ,
197 | size_hint_align=FILL_HORIZ, content=con)
198 | self.addButton.callback_pressed_add(self.addButtonPressed)
199 | self.addButton.disabled = True
200 | self.addButton.show()
201 |
202 | con = Icon(self, size_hint_weight=EXPAND_BOTH,
203 | size_hint_align=FILL_BOTH)
204 | con.standard_set("remove")
205 | con.show()
206 |
207 | self.removeButton = Button(self, size_hint_weight=EXPAND_HORIZ,
208 | size_hint_align=FILL_HORIZ, content=con)
209 | self.removeButton.callback_pressed_add(self.removeButtonPressed)
210 | self.removeButton.disabled = True
211 | self.removeButton.show()
212 |
213 | self.bookmarkModBox.pack_end(self.addButton)
214 | self.bookmarkModBox.pack_end(self.removeButton)
215 |
216 | self.bookmarkBox.pack_end(self.upButton)
217 | self.bookmarkBox.pack_end(self.bookmarksList)
218 | self.bookmarkBox.pack_end(self.bookmarkModBox)
219 |
220 | #Directory List
221 | self.fileListBox = Box(self, size_hint_weight=EXPAND_BOTH,
222 | size_hint_align=FILL_BOTH)
223 | self.fileListBox.show()
224 |
225 | self.fileSortButton = Button(self, size_hint_weight=EXPAND_HORIZ,
226 | size_hint_align=FILL_HORIZ)
227 | self.fileSortButton.text = u"⬆ Name"
228 | self.fileSortButton.callback_pressed_add(self.sortData)
229 | self.fileSortButton.show()
230 |
231 | self.fileList = Genlist(self, size_hint_weight=EXPAND_BOTH,
232 | size_hint_align=FILL_BOTH, homogeneous=True,
233 | mode=ELM_LIST_COMPRESS)
234 | self.fileList.callback_activated_add(self.fileDoubleClicked)
235 | self.fileList.show()
236 |
237 | self.previewImage = previewImage = Image(self)
238 | #previewImage.size_hint_weight = EXPAND_BOTH
239 | previewImage.size_hint_align = FILL_BOTH
240 | previewImage.show()
241 |
242 | self.fileListBox.pack_end(self.fileSortButton)
243 | self.fileListBox.pack_end(self.fileList)
244 | self.fileListBox.pack_end(self.previewImage)
245 |
246 | self.fileSelectorBox.part_content_set("left", self.bookmarkBox)
247 | self.fileSelectorBox.part_content_set("right", self.fileListBox)
248 |
249 | #Cancel and Save/Open button
250 | self.buttonBox = Box(self, size_hint_weight=EXPAND_HORIZ,
251 | size_hint_align=(1.0, 0.5))
252 | self.buttonBox.horizontal = True
253 | self.buttonBox.show()
254 |
255 | self.actionIcon = Icon(self, size_hint_weight=EXPAND_BOTH,
256 | size_hint_align=FILL_BOTH)
257 | self.actionIcon.standard_set("document-save")
258 | self.actionIcon.show()
259 |
260 | self.actionButton = Button(self, size_hint_weight=(0.0, 0.0),
261 | size_hint_align=(1.0, 0.5), content=self.actionIcon)
262 | self.actionButton.text = "Save "
263 | self.actionButton.callback_pressed_add(self.actionButtonPressed)
264 | self.actionButton.show()
265 |
266 | cancelIcon = Icon(self, size_hint_weight=EXPAND_BOTH,
267 | size_hint_align=FILL_BOTH)
268 | cancelIcon.standard_set("exit")
269 | cancelIcon.show()
270 |
271 | self.cancelButton = Button(self, size_hint_weight=(0.0, 0.0),
272 | size_hint_align=(1.0, 0.5), content=cancelIcon)
273 | self.cancelButton.text = "Cancel "
274 | self.cancelButton.callback_pressed_add(self.cancelButtonPressed)
275 | self.cancelButton.show()
276 |
277 | con = Icon(self, size_hint_weight=EXPAND_BOTH,
278 | size_hint_align=FILL_BOTH)
279 | con.standard_set("gtk-find")
280 | con.show()
281 |
282 | self.toggleHiddenButton = Button(self, size_hint_weight=(0.0, 0.0),
283 | size_hint_align=(1.0, 0.5), content=con)
284 | self.toggleHiddenButton.text = "Toggle Hidden "
285 | self.toggleHiddenButton.callback_pressed_add(self.toggleHiddenButtonPressed)
286 | self.toggleHiddenButton.show()
287 |
288 | con = Icon(self, size_hint_weight=EXPAND_BOTH,
289 | size_hint_align=FILL_BOTH)
290 | con.standard_set("folder-new")
291 | con.show()
292 |
293 | self.createFolderButton = Button(self, size_hint_weight=(0.0, 0.0),
294 | size_hint_align=(1.0, 0.5), content=con)
295 | self.createFolderButton.text = "Create Folder "
296 | self.createFolderButton.callback_pressed_add(self.createFolderButtonPressed)
297 | self.createFolderButton.show()
298 |
299 | self.buttonBox.pack_end(self.createFolderButton)
300 | self.buttonBox.pack_end(self.toggleHiddenButton)
301 | self.buttonBox.pack_end(self.cancelButton)
302 | self.buttonBox.pack_end(self.actionButton)
303 |
304 | self.pack_end(self.filenameBox)
305 | self.pack_end(sep)
306 | self.pack_end(self.filepathBox)
307 | self.pack_end(self.autocompleteHover)
308 | self.pack_end(self.fileSelectorBox)
309 | self.pack_end(self.buttonBox)
310 |
311 | self.populateBookmarks()
312 |
313 | self.createPopup = Popup(self)
314 | self.createPopup.part_text_set("title,text", "Create Folder:")
315 |
316 | self.createEn = en = Entry(self, size_hint_weight=EXPAND_HORIZ,
317 | size_hint_align=FILL_HORIZ)
318 | en.single_line_set(True)
319 | en.scrollable_set(True)
320 | en.show()
321 |
322 | self.createPopup.content = en
323 |
324 | bt = Button(self, text="Create")
325 | bt.callback_clicked_add(self.createFolder)
326 | self.createPopup.part_content_set("button1", bt)
327 |
328 | bt2 = Button(self, text="Cancel")
329 | bt2.callback_clicked_add(self.closePopup)
330 | self.createPopup.part_content_set("button2", bt2)
331 |
332 | if defaultPopulate:
333 | self.populateFiles(startPath)
334 |
335 | def folderOnlySet(self, ourValue):
336 | self.folderOnly = ourValue
337 |
338 | if not self.folderOnly:
339 | self.filenameBox.show()
340 | else:
341 | self.filenameBox.hide()
342 |
343 | def createFolder(self, obj):
344 | newDir = "%s%s"%(self.currentDirectory, self.createEn.text)
345 | os.makedirs(newDir)
346 | self.closePopup()
347 | self.populateFiles(self.currentDirectory)
348 |
349 | def createFolderButtonPressed(self, obj):
350 | self.createEn.text = ""
351 | self.createPopup.show()
352 | self.createEn.select_all()
353 |
354 | def closePopup(self, btn=None):
355 | self.createPopup.hide()
356 |
357 | def shutdown(self, obj=None):
358 | self._timer.delete()
359 | self.threadedFunction.shutdown()
360 |
361 | def sortData(self, btn):
362 | self.sortReverse = not self.sortReverse
363 |
364 | if self.sortReverse:
365 | self.fileSortButton.text = u"⬇ Name"
366 | else:
367 | self.fileSortButton.text = u"⬆ Name"
368 |
369 | self.populateFiles(self.currentDirectory)
370 |
371 | def populateBookmarks(self):
372 | con = Icon(self, size_hint_weight=EXPAND_BOTH,
373 | size_hint_align=FILL_BOTH)
374 | con.standard_set("folder_home")
375 | con.show()
376 |
377 | it = self.bookmarksList.item_append("Home", icon=con)
378 | it.data["path"] = self.home
379 |
380 | con = Icon(self, size_hint_weight=EXPAND_BOTH,
381 | size_hint_align=FILL_BOTH)
382 | con.standard_set("drive-harddisk")
383 | con.show()
384 |
385 | it = self.bookmarksList.item_append("Root", icon=con)
386 | it.data["path"] = self.root
387 |
388 | it = self.bookmarksList.item_append("")
389 | it.separator_set(True)
390 |
391 | for bk in self.getGTKBookmarks():
392 | con = Icon(self, size_hint_weight=EXPAND_BOTH,
393 | size_hint_align=FILL_BOTH)
394 | con.standard_set("gtk-directory")
395 | con.show()
396 | it = self.bookmarksList.item_append(bk.split("/")[-1], icon=con)
397 | it.data["path"] = bk[7:]
398 |
399 | def populateFile(self):
400 | pen_file = len(self.pendingFiles)
401 | if pen_file:
402 | for _ in range(int(math.sqrt(pen_file))):
403 | ourPath, d, isDir = self.pendingFiles.popleft()
404 | self.packFileFolder(ourPath, d, isDir)
405 |
406 | #else:
407 | # self._timer.freeze()
408 |
409 | return True
410 |
411 | def populateFiles(self, ourPath):
412 | self.autocompleteHover.hover_end()
413 |
414 | self.pendingFiles.clear()
415 |
416 | if ourPath[:-1] != "/":
417 | ourPath = ourPath + "/"
418 |
419 | if ourPath != self.filepathEntry.text or not self.showHidden:
420 | self.addingHidden = False
421 |
422 | if self.directoryChangeCallback:
423 | self.directoryChangeCallback(ourPath)
424 |
425 | del self.currentSubFolders[:]
426 | del self.currentFiles[:]
427 | self.fileList.clear()
428 | else:
429 | self.addingHidden = True
430 |
431 | self.filepathEntry.text = ourPath.replace("//", "/")
432 | self.currentDirectory = ourPath.replace("//", "/")
433 |
434 | self.threadedFunction.run(self.getFolderContents)
435 | #self._timer.thaw()
436 |
437 | def getFolderContents(self):
438 | ourPath = self.currentDirectory
439 |
440 | try:
441 | data = os.listdir(unicode(ourPath))
442 | except:
443 | data = os.listdir(str(ourPath))
444 |
445 | sortedData = []
446 |
447 | for d in data:
448 | isDir = os.path.isdir("%s%s"%(ourPath, d))
449 |
450 | if isDir:
451 | self.currentSubFolders.append(d)
452 | if self.sortReverse:
453 | sortedData.append([1, d])
454 | else:
455 | sortedData.append([0, d])
456 | else:
457 | self.currentFiles.append(d)
458 | if self.sortReverse:
459 | sortedData.append([0, d])
460 | else:
461 | sortedData.append([1, d])
462 |
463 | sortedData.sort(reverse=self.sortReverse)
464 |
465 | for ourFile in sortedData:
466 | d = ourFile[1]
467 | isDir = ourFile[0] if self.sortReverse else not ourFile[0]
468 | if self.addingHidden and d[0] == ".":
469 | self.pendingFiles.append([ourPath, d, isDir])
470 | elif (d[0] != "." or self.showHidden) and not self.addingHidden:
471 | self.pendingFiles.append([ourPath, d, isDir])
472 |
473 | def packFileFolder(self, ourPath, d, isDir):
474 | if isDir:
475 | li = GenlistItem(item_data={"type": "dir", "path": ourPath, "d": d}, item_class=dirglic, func=self.listItemSelected)
476 | else:
477 | li = GenlistItem(item_data={"type": "file", "path": ourPath, "d": d}, item_class=fileglic, func=self.listItemSelected)
478 |
479 | li.append_to(self.fileList)
480 | #self.fileList.go()
481 | #print("Adding: %s %s %s"%(ourPath, d, isDir))
482 |
483 | def fileDoubleClicked(self, obj, item=None, eventData=None):
484 | if item.data["type"] == "dir":
485 | self.addButton.disabled = True
486 | self.removeButton.disabled = True
487 | self.populateFiles(item.data["path"]+item.text)
488 | else:
489 | self.actionButtonPressed(self.actionButton)
490 |
491 | def getGTKBookmarks(self):
492 | try:
493 | with open(os.path.expanduser('~/.config/gtk-3.0/bookmarks'),'r') as f:
494 | ourBks = []
495 | for x in f:
496 | x = x.split(" ")[0]
497 | x = x.replace("%20", " ")
498 | x = x.strip()
499 | ourBks.append(x)
500 | return ourBks
501 | except IOError:
502 | return []
503 |
504 | def bookmarkDoubleClicked(self, obj, item=None, eventData=None):
505 | item.selected_set(False)
506 | self.addButton.disabled = True
507 | self.removeButton.disabled = True
508 | self.populateFiles(item.data["path"])
509 |
510 | def listItemSelected(self, item, gl, data):
511 | if item.data["type"] == "dir":
512 | self.directorySelected(item)
513 | else:
514 | self.fileSelected(item.text)
515 | item.selected_set(False)
516 |
517 | def fileSelected(self, ourFile):
518 | self.fileEntry.text = ourFile
519 | self.addButton.disabled = True
520 | self.removeButton.disabled = True
521 | self.selectedFolder = None
522 |
523 | #Update image preview if an image is selected
524 | if ourFile[-3:] in ["jpg", "png", "gif"]:
525 | self.previewImage.file_set("%s/%s"%(self.filepathEntry.text, ourFile))
526 | self.previewImage.size_hint_weight = (1.0, 0.4)
527 | else:
528 | self.previewImage.size_hint_weight = (0, 0)
529 |
530 | def directorySelected(self, btn):
531 | ourPath = btn.data["path"]
532 | if btn == self.selectedFolder:
533 | self.populateFiles(ourPath)
534 | self.addButton.disabled = True
535 | else:
536 | self.selectedFolder = btn
537 |
538 | currentMarks = self.getGTKBookmarks()
539 |
540 | toAppend = "file://%s%s"%(self.filepathEntry.text, self.selectedFolder.text)
541 |
542 | if toAppend not in currentMarks:
543 | self.addButton.disabled = False
544 | self.removeButton.disabled = True
545 | else:
546 | self.addButton.disabled = True
547 | self.removeButton.disabled = False
548 |
549 | def upButtonPressed(self, btn):
550 | ourSplit = self.filepathEntry.text.split("/")
551 | del ourSplit[-1]
552 | del ourSplit[-1]
553 | self.populateFiles("/".join(ourSplit))
554 |
555 | def addButtonPressed(self, btn):
556 | toAppend = "file://%s%s"%(self.filepathEntry.text, self.selectedFolder.text.replace(" ", "%20"))
557 |
558 | con = Icon(self, size_hint_weight=EXPAND_BOTH,
559 | size_hint_align=FILL_BOTH)
560 | con.standard_set("gtk-directory")
561 | con.show()
562 | it = self.bookmarksList.item_append(self.selectedFolder.text, icon=con)
563 | it.data["path"] = "%s%s"%(self.filepathEntry.text, self.selectedFolder.text)
564 |
565 | self.bookmarksList.go()
566 |
567 | self.addButton.disabled = True
568 | self.removeButton.disabled = False
569 |
570 | with open(os.path.expanduser('~/.config/gtk-3.0/bookmarks'),'a') as f:
571 | f.write( toAppend + " " + self.selectedFolder.text + "\n" )
572 |
573 | def removeButtonPressed(self, btn):
574 | toRemove = "file://%s%s"%(self.filepathEntry.text, self.selectedFolder.text)
575 |
576 | bks = self.getGTKBookmarks()
577 | bks.remove(toRemove)
578 |
579 | with open(os.path.expanduser('~/.config/gtk-3.0/bookmarks'),'w') as f:
580 | for b in bks:
581 | bName = b.split("/")[-1]
582 | b = b.replace(" ", "%20")
583 | f.write( b + " " + bName + "\n" )
584 |
585 | self.bookmarksList.clear()
586 | self.populateBookmarks()
587 |
588 | self.addButton.disabled = False
589 | self.removeButton.disabled = True
590 |
591 | def setMode(self, ourMode):
592 | self.mode = ourMode.lower()
593 |
594 | self.actionButton.text = "%s "%ourMode
595 | self.actionIcon.standard_set("document-%s"%ourMode.lower())
596 |
597 | if self.mode != "save":
598 | self.createFolderButton.hide()
599 | else:
600 | self.createFolderButton.show()
601 |
602 | def eventsCb(self, obj, src, event_type, event):
603 | if event.modifier_is_set("Control") and event_type == EVAS_CALLBACK_KEY_DOWN:
604 | if event.key.lower() == "l":
605 | self.filepathEntry.focus_set(True)
606 | self.filepathEntry.cursor_end_set()
607 |
608 | def toggleHiddenButtonPressed(self, btn):
609 | self.showHidden = not self.showHidden
610 | self.populateFiles(self.filepathEntry.text)
611 |
612 | def toggleHidden(self):
613 | self.showHidden = not self.showHidden
614 | self.populateFiles(self.filepathEntry.text)
615 |
616 | def callback_cancel_add(self, cb):
617 | self.cancelCallback = cb
618 |
619 | def callback_activated_add(self, cb):
620 | self.actionCallback = cb
621 |
622 | def callback_directory_open_add(self, cb):
623 | self.directoryChangeCallback = cb
624 |
625 | def cancelButtonPressed(self, btn):
626 | if self.cancelCallback:
627 | self.cancelCallback(self)
628 |
629 | def actionButtonPressed(self, btn):
630 | if self.actionCallback:
631 | if not self.folderOnly and self.fileEntry.text:
632 | self.actionCallback(self, "%s%s"%(self.filepathEntry.text, self.fileEntry.text))
633 | elif self.folderOnly:
634 | self.actionCallback(self, "%s"%(self.filepathEntry.text))
635 |
636 | def fileEntryChanged(self, en):
637 | typed = en.text.split("/")[-1]
638 |
639 | newList = []
640 |
641 | self.focusedEntry = en
642 |
643 | if en == self.filepathEntry:
644 | for x in self.currentSubFolders:
645 | if typed in x:
646 | if len(newList) < 10:
647 | newList.append(x)
648 | else:
649 | break
650 | else:
651 | for x in self.currentFiles:
652 | if typed in x:
653 | if len(newList) < 10:
654 | newList.append(x)
655 | else:
656 | break
657 |
658 | if self.autocompleteHover.expanded_get():
659 | self.autocompleteHover.hover_end()
660 |
661 | self.autocompleteHover.clear()
662 |
663 | for x in newList:
664 | self.autocompleteHover.item_add(x)
665 |
666 | self.autocompleteHover.hover_begin()
667 |
668 | def autocompleteSelected(self, hov, item):
669 | hov.hover_end()
670 | if self.focusedEntry == self.filepathEntry:
671 | self.populateFiles("%s%s"%(self.currentDirectory, item.text))
672 | self.filepathEntry.cursor_end_set()
673 | else:
674 | self.fileEntry.text = item.text
675 | self.fileEntry.cursor_end_set()
676 |
677 | def filepathEditDone(self, en):
678 | if os.path.isdir(en.text) and en.text != self.currentDirectory:
679 | self.populateFiles(en.text)
680 | self.filepathEntry.cursor_end_set()
681 | else:
682 | #en.text = self.currentDirectory
683 | pass
684 |
685 | def selected_get(self):
686 | return "%s%s"%(self.filepathEntry.text, self.fileEntry.text)
687 |
--------------------------------------------------------------------------------
/elmextensions/sortedlist.py:
--------------------------------------------------------------------------------
1 | # encoding: utf-8
2 |
3 | from efl.elementary.label import Label
4 | from efl.elementary.box import Box
5 | #from efl.elementary.table import Table
6 | from efl.elementary.panes import Panes
7 | from efl.elementary.button import Button
8 | from efl.elementary.scroller import Scroller, Scrollable, ELM_SCROLLER_POLICY_OFF, ELM_SCROLLER_POLICY_ON, ELM_SCROLLER_POLICY_AUTO
9 | from efl.evas import EVAS_HINT_EXPAND, EVAS_HINT_FILL
10 |
11 | EXPAND_BOTH = EVAS_HINT_EXPAND, EVAS_HINT_EXPAND
12 | EXPAND_HORIZ = EVAS_HINT_EXPAND, 0.0
13 | FILL_BOTH = EVAS_HINT_FILL, EVAS_HINT_FILL
14 | FILL_HORIZ = EVAS_HINT_FILL, 0.5
15 |
16 | class SortedList(Scroller):
17 |
18 | """
19 | A "spread sheet like" widget for elementary.
20 | Argument "titles" is a list, with each element being a tuple:
21 | (, )
22 | """
23 |
24 | def __init__(self, parent_widget, titles=None, initial_sort=0,
25 | ascending=True, *args, **kwargs):
26 | Scroller.__init__(self, parent_widget, *args, **kwargs)
27 | self.policy_set(ELM_SCROLLER_POLICY_AUTO, ELM_SCROLLER_POLICY_OFF)
28 |
29 | self.mainBox = Box(self, size_hint_weight=EXPAND_BOTH,
30 | size_hint_align=FILL_BOTH)
31 | self.mainBox.show()
32 |
33 | self.header = titles
34 | self.sort_column = initial_sort
35 | self.sort_column_ascending = ascending
36 |
37 | self.rows = []
38 | self.header_row = []
39 |
40 | headerPane = Panes(self, size_hint_weight=EXPAND_HORIZ,
41 | size_hint_align=FILL_HORIZ)
42 | headerPane.callback_unpress_add(self.paneResized)
43 | headerPane.show()
44 |
45 | listPane = Panes(self, size_hint_weight=EXPAND_BOTH,
46 | size_hint_align=FILL_BOTH)
47 | listPane.callback_unpress_add(self.paneResized)
48 | listPane.style_set("flush")
49 | listPane.show()
50 |
51 | headerPane.data["related"] = listPane
52 | listPane.data["related"] = headerPane
53 |
54 | self.mainScr = Scroller(self, size_hint_weight=EXPAND_BOTH,
55 | size_hint_align=FILL_BOTH)
56 | self.mainScr.policy_set(ELM_SCROLLER_POLICY_OFF, ELM_SCROLLER_POLICY_AUTO)
57 | self.mainScr.content = listPane
58 | self.mainScr.show()
59 |
60 | self.headerPanes = []
61 | self.headerPanes.append(headerPane)
62 | self.listPanes = []
63 | self.listPanes.append(listPane)
64 | self.lists = []
65 |
66 | #self.pack_end(self.header_box)
67 |
68 | if titles is not None:
69 | self.header_row_pack(titles)
70 |
71 | self.mainBox.pack_end(headerPane)
72 | self.mainBox.pack_end(self.mainScr)
73 |
74 | self.content = self.mainBox
75 | self.show()
76 |
77 | def header_row_pack(self, titles):
78 |
79 | """Takes a list (or a tuple) of tuples (string, bool, int) and packs them to
80 | the first row of the table."""
81 |
82 | assert isinstance(titles, (list, tuple))
83 | for t in titles:
84 | assert isinstance(t, tuple)
85 | assert len(t) == 2
86 | title, sortable = t
87 | try:
88 | assert isinstance(title, basestring)
89 | except:
90 | assert isinstance(title, str)
91 | assert isinstance(sortable, bool)
92 |
93 | def sort_btn_cb(button, col):
94 | if self.sort_column == col:
95 | self.reverse()
96 | else:
97 | self.sort_by_column(col)
98 |
99 | titleCount = len(titles)
100 | for count, t in enumerate(titles):
101 | title, sortable = t
102 | btn = Button(self, size_hint_weight=EXPAND_HORIZ,
103 | size_hint_align=FILL_HORIZ, text=title)
104 | btn.callback_clicked_add(sort_btn_cb, count)
105 | if not sortable:
106 | btn.disabled = True
107 | btn.show()
108 | self.header_row.append(btn)
109 |
110 | bx = Box(self, size_hint_weight=EXPAND_BOTH,
111 | size_hint_align=FILL_BOTH)
112 | bx.show()
113 |
114 | if len(self.listPanes) < titleCount:
115 | wdth = 1.0 / (titleCount - count)
116 | self.listPanes[count].part_content_set("left", bx)
117 | self.listPanes[count].content_left_size = wdth
118 |
119 | nextList = Panes(self, size_hint_weight=EXPAND_BOTH,
120 | size_hint_align=FILL_BOTH)
121 | nextList.callback_unpress_add(self.paneResized)
122 | nextList.style_set("flush")
123 | nextList.show()
124 |
125 | self.listPanes[count].part_content_set("right", nextList)
126 | self.listPanes.append(nextList)
127 |
128 | self.headerPanes[count].part_content_set("left", btn)
129 | self.headerPanes[count].content_left_size = wdth
130 |
131 | nextHeader = Panes(self, size_hint_weight=EXPAND_HORIZ,
132 | size_hint_align=FILL_HORIZ)
133 | nextHeader.callback_unpress_add(self.paneResized)
134 | nextHeader.show()
135 |
136 | self.headerPanes[count].part_content_set("right", nextHeader)
137 | self.headerPanes.append(nextHeader)
138 |
139 | nextList.data["related"] = nextHeader
140 | nextHeader.data["related"] = nextList
141 | else:
142 | self.listPanes[count - 1].part_content_set("right", bx)
143 | self.headerPanes[count - 1].part_content_set("right", btn)
144 |
145 | self.lists.append(bx)
146 |
147 | def paneResized(self, obj):
148 | leftSize = obj.content_left_size
149 | rightSize = obj.content_right_size
150 | related = obj.data["related"]
151 |
152 | related.content_left_size = leftSize
153 | related.content_right_size = rightSize
154 |
155 | def row_pack(self, row, sort=True):
156 |
157 | """Takes a list of items and packs them to the table."""
158 |
159 | assert len(row) == len(self.header_row), (
160 | "The row you are trying to add to this sorted list has the wrong "
161 | "number of items! expected: %i got: %i" % (
162 | len(self.header_row), len(row)
163 | )
164 | )
165 |
166 | self.rows.append(row)
167 | self.add_row(row)
168 |
169 | if sort:
170 | self.sort_by_column(self.sort_column)
171 |
172 | def add_row(self, row):
173 | #print("Test %s"%row)
174 | for count, item in enumerate(row):
175 | self.lists[count].pack_end(item)
176 |
177 | def row_unpack(self, row, delete=False):
178 |
179 | """Unpacks and hides, and optionally deletes, a row of items.
180 | The argument row can either be the row itself or its index number.
181 | """
182 | if isinstance(row, int):
183 | row_index = row
184 | else:
185 | row_index = self.rows.index(row)+1
186 |
187 | # print("row index: " + str(row_index-1))
188 | # print("length: " + str(len(self.rows)))
189 | # print("sort_data: " + str(row[self.sort_column].data["sort_data"]))
190 |
191 | row = self.rows.pop(row_index-1)
192 |
193 | for count, item in enumerate(row):
194 | self.lists[count].unpack(item)
195 | if delete:
196 | item.delete()
197 | else:
198 | item.hide()
199 |
200 | self.sort_by_column(self.sort_column,
201 | ascending=self.sort_column_ascending)
202 |
203 | def unpack_all(self):
204 | tmplist = list(self.rows)
205 | for rw in tmplist:
206 | self.row_unpack(rw)
207 |
208 | def reverse(self):
209 | rev_order = reversed(list(range(len(self.rows))))
210 | for bx in self.lists:
211 | bx.unpack_all()
212 |
213 | for new_y in rev_order:
214 | self.add_row(self.rows[new_y])
215 |
216 | lb = self.header_row[self.sort_column].part_content_get("icon")
217 | if lb is not None:
218 | if self.sort_column_ascending:
219 | lb.text = u"⬆"
220 | self.sort_column_ascending = False
221 | else:
222 | lb.text = u"⬇"
223 | self.sort_column_ascending = True
224 |
225 | self.rows.reverse()
226 |
227 | def sort_by_column(self, col, ascending=True):
228 |
229 | assert col >= 0
230 | assert col < len(self.header_row)
231 |
232 | self.header_row[self.sort_column].icon = None
233 |
234 | btn = self.header_row[col]
235 | ic = Label(btn)
236 | btn.part_content_set("icon", ic)
237 | ic.show()
238 |
239 | if ascending == True: #ascending:
240 | ic.text = u"⬇"
241 | self.sort_column_ascending = True
242 | else:
243 | ic.text = u"⬆"
244 | self.sort_column_ascending = False
245 |
246 | orig_col = [
247 | (i, x[col].data.get("sort_data", x[col].text)) \
248 | for i, x in enumerate(self.rows)
249 | ]
250 | sorted_col = sorted(orig_col, key=lambda e: e[1])
251 | new_order = [x[0] for x in sorted_col]
252 |
253 | # print(new_order)
254 |
255 | if not ascending:
256 | new_order.reverse()
257 |
258 | # print(new_order)
259 |
260 | for bx in self.lists:
261 | bx.unpack_all()
262 |
263 | for new_y in new_order:
264 | self.add_row(self.rows[new_y])
265 |
266 | self.rows.sort(
267 | key=lambda e: e[col].data.get("sort_data", e[col].text),
268 | #reverse=False if ascending else True
269 | )
270 | self.sort_column = col
271 |
272 | def update(self):
273 | self.sort_by_column(self.sort_column, self.sort_column_ascending)
274 |
--------------------------------------------------------------------------------
/elmextensions/tabbedbox.py:
--------------------------------------------------------------------------------
1 | # encoding: utf-8
2 |
3 | from efl.evas import EVAS_HINT_EXPAND, EVAS_HINT_FILL
4 | from efl.elementary.box import Box
5 | from efl.elementary.button import Button
6 | from efl.elementary.icon import Icon
7 | from efl.elementary.separator import Separator
8 | from efl.elementary.scroller import Scroller
9 | from efl.elementary.naviframe import Naviframe
10 |
11 | EXPAND_BOTH = EVAS_HINT_EXPAND, EVAS_HINT_EXPAND
12 | EXPAND_HORIZ = EVAS_HINT_EXPAND, 0.0
13 | FILL_BOTH = EVAS_HINT_FILL, EVAS_HINT_FILL
14 | FILL_HORIZ = EVAS_HINT_FILL, 0.5
15 | EXPAND_NONE = 0.0, 0.0
16 | ALIGN_CENTER = 0.5, 0.5
17 | ALIGN_RIGHT = 1.0, 0.5
18 | ALIGN_LEFT = 0.0, 0.5
19 |
20 | class TabbedBox(Box):
21 | def __init__(self, parent_widget, *args, **kwargs):
22 | Box.__init__(self, parent_widget, *args, **kwargs)
23 |
24 | self.tabs = []
25 | self.currentTab = None
26 | self.tabChangedCallback = None
27 | self.closeCallback = None
28 | self.emptyCallback = None
29 |
30 | self.scr = Scroller(self, size_hint_weight=EXPAND_HORIZ,
31 | size_hint_align=FILL_BOTH)
32 | self.scr.content_min_limit(False, True)
33 |
34 | self.buttonBox = Box(self.scr, size_hint_weight=EXPAND_HORIZ,
35 | align=ALIGN_LEFT)
36 | self.buttonBox.horizontal = True
37 | self.buttonBox.show()
38 |
39 | self.scr.content = self.buttonBox
40 | self.scr.show()
41 |
42 | self.nf = Naviframe(self, size_hint_weight=EXPAND_BOTH,
43 | size_hint_align=FILL_BOTH)
44 | self.nf.show()
45 |
46 | self.pack_end(self.scr)
47 | self.pack_end(self.nf)
48 |
49 | def addTab(self, widget, tabName, canClose=True, disabled=False):
50 | self.tabs.append(widget)
51 |
52 | btn = Button(self.buttonBox, style="anchor", size_hint_align=ALIGN_LEFT)
53 | btn.text = tabName
54 | btn.data["widget"] = widget
55 | btn.disabled = disabled
56 | btn.callback_clicked_add(self.showTab, widget)
57 | btn.show()
58 |
59 | icn = Icon(self.buttonBox)
60 | icn.standard_set("gtk-close")
61 | icn.show()
62 |
63 | cls = Button(self.buttonBox, content=icn, style="anchor", size_hint_align=ALIGN_LEFT)
64 | cls.data["widget"] = widget
65 | cls.callback_clicked_add(self.closeTab)
66 | cls.disabled = disabled
67 | if canClose:
68 | cls.show()
69 |
70 | sep = Separator(self.buttonBox, size_hint_align=ALIGN_LEFT)
71 | sep.show()
72 |
73 | self.buttonBox.pack_end(btn)
74 | self.buttonBox.pack_end(cls)
75 | self.buttonBox.pack_end(sep)
76 |
77 | #Arguments go: btn, cls, sep
78 | widget.data["close"] = cls
79 | widget.data["button"] = btn
80 | widget.data["sep"] = sep
81 |
82 | self.showTab(widget=widget)
83 |
84 | def disableTab(self, tabIndex):
85 | btn, cls = self.tabs[tabIndex].data["button"], self.tabs[tabIndex].data["close"]
86 | btn.disabled = True
87 | cls.disabled = True
88 |
89 | def enableTab(self, tabIndex):
90 | btn, cls = self.tabs[tabIndex].data["button"], self.tabs[tabIndex].data["close"]
91 | btn.disabled = False
92 | cls.disabled = False
93 |
94 | def showTab(self, btn=None, widget=None):
95 | if type(btn) is int:
96 | widget = self.tabs[btn]
97 | if widget != self.currentTab:
98 | if self.currentTab:
99 | self.currentTab.data["button"].style="anchor"
100 | self.nf.item_simple_push(widget)
101 | self.currentTab = widget
102 | self.currentTab.data["button"].style="widget"
103 |
104 | if self.tabChangedCallback:
105 | self.tabChangedCallback(self, widget)
106 |
107 | def closeTab(self, btn):
108 | if not self.closeCallback:
109 | self.deleteTab(btn.data["widget"])
110 | else:
111 | self.closeCallback(self, btn.data["widget"])
112 |
113 | def deleteTab(self, widget):
114 | if type(widget) is int:
115 | widget = self.tabs[widget]
116 |
117 | del self.tabs[self.tabs.index(widget)]
118 |
119 | self.buttonBox.unpack(widget.data["close"])
120 | self.buttonBox.unpack(widget.data["button"])
121 | self.buttonBox.unpack(widget.data["sep"])
122 |
123 | widget.data["close"].delete()
124 | widget.data["button"].delete()
125 | widget.data["sep"].delete()
126 | widget.delete()
127 |
128 | if self.currentTab == widget and len(self.tabs):
129 | self.showTab(widget=self.tabs[0])
130 |
131 | if not len(self.tabs) and self.emptyCallback:
132 | self.emptyCallback(self)
133 |
--------------------------------------------------------------------------------
/license.txt:
--------------------------------------------------------------------------------
1 | This license applies to all files in this repository that do not have
2 | another license otherwise indicated.
3 |
4 | Copyright (c) 2014, Jeff Hoogland
5 | All rights reserved.
6 |
7 | Redistribution and use in source and binary forms, with or without
8 | modification, are permitted provided that the following conditions are met:
9 | * Redistributions of source code must retain the above copyright
10 | notice, this list of conditions and the following disclaimer.
11 | * Redistributions in binary form must reproduce the above copyright
12 | notice, this list of conditions and the following disclaimer in the
13 | documentation and/or other materials provided with the distribution.
14 | * Neither the name of the nor the
15 | names of its contributors may be used to endorse or promote products
16 | derived from this software without specific prior written permission.
17 |
18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 | DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY
22 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | from distutils.core import setup
3 | from efl.utils.setup import build_extra, uninstall
4 | from elmextensions import __version__
5 |
6 |
7 | setup(
8 | name = 'python-elm-extensions',
9 | version = __version__,
10 | description = 'A python module of useful elementary objects.',
11 | license="BSD-3-clause",
12 | author = 'Jeff Hoogland',
13 | author_email = 'JeffHoogland@Linux.com',
14 | url="https://github.com/JeffHoogland/python-elm-extensions",
15 | requires = ['efl (>= 1.19)'],
16 | provides = ['elmextensions'],
17 | packages = ['elmextensions'],
18 | cmdclass = {
19 | 'build': build_extra,
20 | 'uninstall': uninstall,
21 | },
22 | command_options={
23 | 'install': {'record': ('setup.py', 'installed_files.txt')}
24 | },
25 | )
--------------------------------------------------------------------------------
/sortedlistother/sortedgenlist.py:
--------------------------------------------------------------------------------
1 | # encoding: utf-8
2 |
3 | from efl.elementary.genlist import Genlist, GenlistItem, GenlistItemClass, ELM_LIST_EXPAND
4 | from efl.elementary.label import Label
5 | from efl.elementary.box import Box
6 | from efl.elementary.button import Button
7 | from efl.elementary.separator import Separator
8 | from efl.elementary.scroller import Scroller, Scrollable, ELM_SCROLLER_POLICY_OFF, ELM_SCROLLER_POLICY_ON, ELM_SCROLLER_POLICY_AUTO
9 | from efl.evas import EVAS_HINT_EXPAND, EVAS_HINT_FILL
10 |
11 | EXPAND_BOTH = EVAS_HINT_EXPAND, EVAS_HINT_EXPAND
12 | EXPAND_HORIZ = EVAS_HINT_EXPAND, 0.0
13 | FILL_BOTH = EVAS_HINT_FILL, EVAS_HINT_FILL
14 | FILL_HORIZ = EVAS_HINT_FILL, 0.5
15 |
16 | class SortedList(Box):
17 |
18 | """
19 |
20 | A "spread sheet like" widget for elementary.
21 |
22 | Argument "titles" is a list, with each element being a tuple:
23 | (, )
24 |
25 | """
26 |
27 | def __init__(self, parent_widget, titles=None, initial_sort=0,
28 | ascending=True, *args, **kwargs):
29 | Box.__init__(self, parent_widget, *args, **kwargs)
30 |
31 | self.header = titles
32 | self.sort_column = initial_sort
33 | self.sort_column_ascending = ascending
34 |
35 | self.rows = []
36 | self.header_row = []
37 | self.header_box = Box(self, size_hint_weight=EXPAND_HORIZ,
38 | size_hint_align=FILL_HORIZ)
39 | self.header_box.horizontal = True
40 | self.header_box.show()
41 |
42 | scr = Scroller(self, size_hint_weight=EXPAND_BOTH,
43 | size_hint_align=FILL_BOTH)
44 |
45 | self.list_box = Box(self, size_hint_weight=EXPAND_BOTH,
46 | size_hint_align=FILL_BOTH)
47 | self.list_box.horizontal = True
48 | self.list_box.show()
49 |
50 | scr.policy_set(ELM_SCROLLER_POLICY_OFF, ELM_SCROLLER_POLICY_ON)
51 | scr.content = self.list_box
52 | scr.show()
53 |
54 | self.lists = []
55 |
56 | self.pack_end(self.header_box)
57 | self.pack_end(scr)
58 | self.show()
59 |
60 | if titles is not None:
61 | self.header_row_pack(titles)
62 |
63 | def header_row_pack(self, titles):
64 |
65 | """Takes a list (or a tuple) of tuples (string, bool) and packs them to
66 | the first row of the table."""
67 |
68 | assert isinstance(titles, (list, tuple))
69 | for t in titles:
70 | assert isinstance(t, tuple)
71 | assert len(t) == 2
72 | title, sortable = t
73 | try:
74 | assert isinstance(title, basestring)
75 | except:
76 | assert isinstance(title, str)
77 | assert isinstance(sortable, bool)
78 |
79 | def sort_btn_cb(button, col):
80 | if self.sort_column == col:
81 | self.reverse()
82 | else:
83 | self.sort_by_column(col)
84 |
85 | for count, t in enumerate(titles):
86 | title, sortable = t
87 | btn = Button(self, size_hint_weight=EXPAND_HORIZ,
88 | size_hint_align=FILL_HORIZ, text=title)
89 | btn.callback_clicked_add(sort_btn_cb, count)
90 | if not sortable:
91 | btn.disabled = True
92 | btn.show()
93 | self.header_box.pack_end(btn)
94 | self.header_row.append(btn)
95 |
96 | elm_list = ScrollableGenlist(self, size_hint_weight=EXPAND_BOTH, size_hint_align=FILL_BOTH)
97 | elm_list.policy_set(ELM_SCROLLER_POLICY_AUTO, ELM_SCROLLER_POLICY_OFF)
98 | elm_list.mode_set(ELM_LIST_EXPAND)
99 | #elm_list.go()
100 | elm_list.show()
101 | self.list_box.pack_end(elm_list)
102 | self.lists.append(elm_list)
103 |
104 | sep = Separator(self)
105 | sep.show()
106 |
107 | self.header_box.pack_end(sep)
108 | self.header_box.pack_end(sep)
109 |
110 | def row_pack(self, row, sort=True):
111 |
112 | """Takes a list of items and packs them to the table."""
113 |
114 | assert len(row) == len(self.header_row), (
115 | "The row you are trying to add to this sorted list has the wrong "
116 | "number of items! expected: %i got: %i" % (
117 | len(self.header_row), len(row)
118 | )
119 | )
120 |
121 | self.rows.append(row)
122 | self.add_row(row)
123 |
124 | if sort:
125 | self.sort_by_column(self.sort_column)
126 |
127 | def add_row(self, row):
128 | def gl_text_get(obj, part, item_data):
129 | return item_data
130 |
131 | def gl_content_get(obj, part, data):
132 | pass
133 |
134 | def gl_state_get(obj, part, item_data):
135 | return False
136 |
137 | def gl_item_sel(gli, gl, *args, **kwargs):
138 | print("\n---GenlistItem selected---")
139 | print(gli)
140 | print(gl)
141 | print(args)
142 | print(kwargs)
143 | print(("item_data: %s" % gli.data_get()))
144 |
145 | itc = GenlistItemClass(item_style="default",
146 | text_get_func=gl_text_get,
147 | content_get_func=gl_content_get,
148 | state_get_func=gl_state_get)
149 |
150 | for count, item in enumerate(row):
151 | self.lists[count].item_append(itc, str(item), func=gl_item_sel)
152 |
153 | def row_unpack(self, row, delete=False):
154 |
155 | """Unpacks and hides, and optionally deletes, a row of items.
156 |
157 | The argument row can either be the row itself or its index number.
158 |
159 | """
160 | if isinstance(row, int):
161 | row_index = row
162 | else:
163 | row_index = self.rows.index(row) - 1
164 |
165 | # print("row index: " + str(row_index-1))
166 | # print("length: " + str(len(self.rows)))
167 | # print("sort_data: " + str(row[self.sort_column].data["sort_data"]))
168 |
169 | row = self.rows.pop(row_index)
170 |
171 | self.sort_by_column(self.sort_column,
172 | ascending=self.sort_column_ascending)
173 |
174 | def reverse(self):
175 | self.rows.reverse()
176 | for our_list in self.lists:
177 | our_list.clear()
178 | for row in self.rows:
179 | self.add_row(row)
180 |
181 | lb = self.header_row[self.sort_column].part_content_get("icon")
182 | if lb is not None:
183 | if self.sort_column_ascending:
184 | lb.text = u"⬆"
185 | self.sort_column_ascending = False
186 | else:
187 | lb.text = u"⬇"
188 | self.sort_column_ascending = True
189 |
190 | def sort_by_column(self, col, ascending=True):
191 |
192 | assert col >= 0
193 | assert col < len(self.header_row)
194 |
195 | self.header_row[self.sort_column].icon = None
196 |
197 | btn = self.header_row[col]
198 | ic = Label(btn)
199 | btn.part_content_set("icon", ic)
200 | ic.show()
201 |
202 | if ascending == True: #ascending:
203 | ic.text = u"⬇"
204 | self.sort_column_ascending = True
205 | else:
206 | ic.text = u"⬆"
207 | self.sort_column_ascending = False
208 |
209 |
210 | self.rows.sort(
211 | key=lambda e: e[col],
212 | #reverse=False if ascending else True
213 | )
214 |
215 | if not ascending:
216 | self.rows.reverse()
217 |
218 | #Clear old data
219 | for our_list in self.lists:
220 | our_list.clear()
221 |
222 | for row in self.rows:
223 | self.add_row(row)
224 |
225 | self.sort_column = col
226 |
227 | def update(self):
228 | self.sort_by_column(self.sort_column, self.sort_column_ascending)
229 |
230 | class ScrollableGenlist(Genlist, Scrollable):
231 | def __init__(self, canvas, *args, **kwargs):
232 | Genlist.__init__(self, canvas, *args, **kwargs)
233 |
--------------------------------------------------------------------------------
/sortedlistother/sortedlist.py:
--------------------------------------------------------------------------------
1 | # encoding: utf-8
2 |
3 | from efl.elementary.list import List, ELM_LIST_LIMIT, ELM_LIST_COMPRESS, ELM_LIST_EXPAND
4 | from efl.elementary.label import Label
5 | from efl.elementary.box import Box
6 | from efl.elementary.check import Check
7 | from efl.elementary.button import Button
8 | from efl.elementary.separator import Separator
9 | from efl.elementary.scroller import Scroller, Scrollable, ELM_SCROLLER_POLICY_OFF, ELM_SCROLLER_POLICY_ON, ELM_SCROLLER_POLICY_AUTO
10 | from efl.evas import EVAS_HINT_EXPAND, EVAS_HINT_FILL
11 |
12 | EXPAND_BOTH = EVAS_HINT_EXPAND, EVAS_HINT_EXPAND
13 | EXPAND_HORIZ = EVAS_HINT_EXPAND, 0.0
14 | FILL_BOTH = EVAS_HINT_FILL, EVAS_HINT_FILL
15 | FILL_HORIZ = EVAS_HINT_FILL, 0.5
16 |
17 | class SortedList(Box):
18 |
19 | """
20 |
21 | A "spread sheet like" widget for elementary.
22 |
23 | Argument "titles" is a list, with each element being a tuple:
24 | (, )
25 |
26 | """
27 |
28 | def __init__(self, parent_widget, titles=None, initial_sort=None,
29 | ascending=True, *args, **kwargs):
30 | Box.__init__(self, parent_widget, *args, **kwargs)
31 |
32 | self.header = titles
33 | self.sort_column = initial_sort
34 | self.sort_column_ascending = ascending
35 |
36 | self.rows = []
37 | self.header_row = []
38 | self.header_box = Box(self, size_hint_weight=EXPAND_HORIZ,
39 | size_hint_align=FILL_HORIZ)
40 | self.header_box.horizontal = True
41 | self.header_box.show()
42 |
43 | scr = Scroller(self, size_hint_weight=EXPAND_BOTH,
44 | size_hint_align=FILL_BOTH)
45 |
46 | self.list_box = Box(self, size_hint_weight=EXPAND_BOTH,
47 | size_hint_align=FILL_BOTH)
48 | self.list_box.horizontal = True
49 | self.list_box.show()
50 |
51 | scr.policy_set(ELM_SCROLLER_POLICY_OFF, ELM_SCROLLER_POLICY_ON)
52 | scr.content = self.list_box
53 | scr.show()
54 |
55 | self.lists = []
56 |
57 | self.pack_end(self.header_box)
58 | self.pack_end(scr)
59 | self.show()
60 |
61 | if titles is not None:
62 | self.header_row_pack(titles)
63 |
64 | def header_row_pack(self, titles):
65 |
66 | """Takes a list (or a tuple) of tuples (string, bool) and packs them to
67 | the first row of the table."""
68 |
69 | assert isinstance(titles, (list, tuple))
70 | for t in titles:
71 | assert isinstance(t, tuple)
72 | assert len(t) == 2
73 | title, sortable = t
74 | try:
75 | assert isinstance(title, basestring)
76 | except:
77 | assert isinstance(title, str)
78 | assert isinstance(sortable, bool)
79 |
80 | def sort_btn_cb(button, col):
81 | if self.sort_column == col:
82 | self.reverse()
83 | else:
84 | self.sort_by_column(col)
85 |
86 | for count, t in enumerate(titles):
87 | title, sortable = t
88 | btn = Button(self, size_hint_weight=EXPAND_HORIZ,
89 | size_hint_align=FILL_HORIZ, text=title)
90 | btn.callback_clicked_add(sort_btn_cb, count)
91 | if not sortable:
92 | btn.disabled = True
93 | btn.show()
94 | self.header_box.pack_end(btn)
95 | self.header_row.append(btn)
96 |
97 | elm_list = ScrollableList(self, size_hint_weight=EXPAND_BOTH, size_hint_align=FILL_BOTH)
98 | elm_list.policy_set(ELM_SCROLLER_POLICY_AUTO, ELM_SCROLLER_POLICY_OFF)
99 | elm_list.mode_set(ELM_LIST_EXPAND)
100 | elm_list.go()
101 | elm_list.show()
102 | self.list_box.pack_end(elm_list)
103 | self.lists.append(elm_list)
104 |
105 | sep = Separator(self)
106 | sep.show()
107 |
108 | self.header_box.pack_end(sep)
109 | self.header_box.pack_end(sep)
110 |
111 | def row_pack(self, row, sort=True):
112 |
113 | """Takes a list of items and packs them to the table."""
114 |
115 | assert len(row) == len(self.header_row), (
116 | "The row you are trying to add to this sorted list has the wrong "
117 | "number of items! expected: %i got: %i" % (
118 | len(self.header_row), len(row)
119 | )
120 | )
121 |
122 | self.rows.append(row)
123 | self.add_row(row)
124 |
125 | if sort:
126 | self.sort_by_column(self.sort_column)
127 |
128 | def add_row(self, row):
129 | for count, item in enumerate(row):
130 | #check = Button(self)
131 | #check.show()
132 | self.lists[count].item_append(str(item))
133 |
134 | def row_unpack(self, row, delete=False):
135 |
136 | """Unpacks and hides, and optionally deletes, a row of items.
137 |
138 | The argument row can either be the row itself or its index number.
139 |
140 | """
141 | if isinstance(row, int):
142 | row_index = row
143 | else:
144 | row_index = self.rows.index(row)
145 |
146 | # print("row index: " + str(row_index-1))
147 | # print("length: " + str(len(self.rows)))
148 | # print("sort_data: " + str(row[self.sort_column].data["sort_data"]))
149 |
150 | row = self.rows.pop(row_index)
151 |
152 | self.sort_by_column(self.sort_column,
153 | ascending=self.sort_column_ascending)
154 |
155 | def reverse(self):
156 | self.rows.reverse()
157 | for our_list in self.lists:
158 | our_list.clear()
159 | for row in self.rows:
160 | self.add_row(row)
161 |
162 | lb = self.header_row[self.sort_column].part_content_get("icon")
163 | if lb is not None:
164 | if self.sort_column_ascending:
165 | lb.text = u"⬆"
166 | self.sort_column_ascending = False
167 | else:
168 | lb.text = u"⬇"
169 | self.sort_column_ascending = True
170 |
171 | def sort_by_column(self, col, ascending=True):
172 |
173 | assert col >= 0
174 | assert col < len(self.header_row)
175 |
176 | if self.sort_column:
177 | self.header_row[self.sort_column].icon = None
178 |
179 | btn = self.header_row[col]
180 | ic = Label(btn)
181 | btn.part_content_set("icon", ic)
182 | ic.show()
183 |
184 | if ascending == True: #ascending:
185 | ic.text = u"⬇"
186 | self.sort_column_ascending = True
187 | else:
188 | ic.text = u"⬆"
189 | self.sort_column_ascending = False
190 |
191 |
192 | self.rows.sort(
193 | key=lambda e: e[col],
194 | #reverse=False if ascending else True
195 | )
196 |
197 | if not ascending:
198 | self.rows.reverse()
199 |
200 | #Clear old data
201 | for our_list in self.lists:
202 | our_list.clear()
203 |
204 | for row in self.rows:
205 | self.add_row(row)
206 |
207 | self.sort_column = col
208 |
209 | def update(self):
210 | self.sort_by_column(self.sort_column, self.sort_column_ascending)
211 |
212 | class ScrollableList(List, Scrollable):
213 | def __init__(self, canvas, *args, **kwargs):
214 | List.__init__(self, canvas, *args, **kwargs)
215 |
--------------------------------------------------------------------------------
/sortedlistother/test_sortedlist.py:
--------------------------------------------------------------------------------
1 | import random
2 |
3 | import efl.elementary as elm
4 | elm.init()
5 | from efl.elementary.window import StandardWindow
6 | from efl.elementary.label import Label
7 | from efl.elementary.button import Button
8 | from efl.evas import EVAS_HINT_EXPAND, EVAS_HINT_FILL
9 |
10 | from sortedlist import SortedList
11 |
12 | EXPAND_BOTH = EVAS_HINT_EXPAND, EVAS_HINT_EXPAND
13 | FILL_BOTH = EVAS_HINT_FILL, EVAS_HINT_FILL
14 |
15 | ROWS = 300
16 | COLUMNS = 5
17 |
18 | class derp(object):
19 | def __init__( self ):
20 | win = StandardWindow("Testing", "Elementary Sorted Table")
21 | win.callback_delete_request_add(lambda o: elm.exit())
22 |
23 | titles = []
24 | for i in range(COLUMNS):
25 | titles.append(
26 | ("Column " + str(i), True if i != 2 else False)
27 | )
28 |
29 | slist = SortedList(win, titles=titles, size_hint_weight=EXPAND_BOTH,
30 | size_hint_align=FILL_BOTH)
31 |
32 | for i in range(ROWS):
33 | row = []
34 | for j in range(COLUMNS):
35 | data = random.randint(0, ROWS*COLUMNS)
36 | row.append(data)
37 | slist.row_pack(row, sort=False)
38 | #slist.sort_by_column(1)
39 | slist.show()
40 |
41 | win.resize_object_add(slist)
42 |
43 | win.resize(600, 400)
44 | win.show()
45 |
46 | if __name__ == "__main__":
47 | GUI = derp()
48 | elm.run()
49 | elm.shutdown()
50 |
--------------------------------------------------------------------------------
/test_aboutwindow.py:
--------------------------------------------------------------------------------
1 | import efl.elementary as elm
2 | from efl.elementary.window import StandardWindow
3 | from efl.elementary.label import Label
4 | elm.init()
5 | from efl.evas import EVAS_HINT_EXPAND, EVAS_HINT_FILL
6 |
7 | EXPAND_BOTH = EVAS_HINT_EXPAND, EVAS_HINT_EXPAND
8 | FILL_BOTH = EVAS_HINT_FILL, EVAS_HINT_FILL
9 |
10 | from elmextensions import AboutWindow
11 |
12 | AUTHORS = """
13 |
14 |
15 | Jeff Hoogland (Jef91)
16 | Contact
17 |
18 | Wolfgang Morawetz (wfx)
19 |
20 | Kai Huuhko (kukko)
21 |
22 | """
23 |
24 | LICENSE = """
25 |
26 |
27 | GNU GENERAL PUBLIC LICENSE
28 | Version 3, 29 June 2007
29 |
30 |
31 | This program is free software: you can redistribute it and/or modify
32 | it under the terms of the GNU General Public License as published by
33 | the Free Software Foundation, either version 3 of the License, or
34 | (at your option) any later version.
35 |
36 | This program is distributed in the hope that it will be useful,
37 | but WITHOUT ANY WARRANTY; without even the implied warranty of
38 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
39 | GNU General Public License for more details.
40 |
41 | You should have received a copy of the GNU General Public License
42 | along with this program. If not, see
43 | http://www.gnu.org/licenses/
44 |
45 | """
46 |
47 | INFO = """
48 |
49 | Elementary Python Extensions are awesome!
50 |
51 |
52 |
53 | """
54 |
55 | class MainWindow(object):
56 | def __init__( self ):
57 | win = StandardWindow("Testing", "Elementary About Dialog")
58 | win.callback_delete_request_add(lambda o: elm.exit())
59 | win.show()
60 |
61 | lbl = Label(win, size_hint_weight=EXPAND_BOTH,
62 | size_hint_align=FILL_BOTH)
63 | lbl.text = "This is a parent window for the About Dialog. Close when done."
64 | lbl.show()
65 |
66 | win.resize_object_add(lbl)
67 |
68 | win.resize(600, 400)
69 | win.show()
70 |
71 | AboutWindow(win, title="About Test", standardicon="dialog-information", \
72 | version="1.0", authors=AUTHORS, \
73 | licen=LICENSE, webaddress="https://github.com/JeffHoogland/python-elm-extensions", \
74 | info=INFO)
75 |
76 | if __name__ == "__main__":
77 | GUI = MainWindow()
78 | elm.run()
79 | elm.shutdown()
80 |
--------------------------------------------------------------------------------
/test_embeddedterminal.py:
--------------------------------------------------------------------------------
1 | import random
2 |
3 | import efl.elementary as elm
4 | elm.init()
5 | from efl.elementary.window import StandardWindow
6 | from efl.elementary.label import Label
7 | from efl.elementary.button import Button
8 | from efl.evas import EVAS_HINT_EXPAND, EVAS_HINT_FILL
9 |
10 | from elmextensions import EmbeddedTerminal
11 |
12 | EXPAND_BOTH = EVAS_HINT_EXPAND, EVAS_HINT_EXPAND
13 | FILL_BOTH = EVAS_HINT_FILL, EVAS_HINT_FILL
14 |
15 | class MainWindow(object):
16 | def __init__( self ):
17 | win = StandardWindow("Testing", "Elementary Embedded Terminal")
18 | win.callback_delete_request_add(lambda o: elm.exit())
19 |
20 | term = EmbeddedTerminal(win, size_hint_weight=EXPAND_BOTH, size_hint_align=FILL_BOTH)
21 |
22 | term.show()
23 |
24 | win.resize_object_add(term)
25 |
26 | win.resize(600, 400)
27 | win.show()
28 |
29 | if __name__ == "__main__":
30 | GUI = MainWindow()
31 | elm.run()
32 | elm.shutdown()
33 |
--------------------------------------------------------------------------------
/test_fileselector.py:
--------------------------------------------------------------------------------
1 | import random
2 |
3 | import efl.elementary as elm
4 | elm.init()
5 | from efl.elementary.window import StandardWindow
6 | from efl.elementary.label import Label
7 | from efl.elementary.button import Button
8 | from efl.evas import EVAS_HINT_EXPAND, EVAS_HINT_FILL
9 |
10 | from elmextensions import FileSelector
11 |
12 | EXPAND_BOTH = EVAS_HINT_EXPAND, EVAS_HINT_EXPAND
13 | FILL_BOTH = EVAS_HINT_FILL, EVAS_HINT_FILL
14 |
15 | class MainWindow(object):
16 | def __init__( self ):
17 | win = StandardWindow("Testing", "Elementary File Selector")
18 | win.callback_delete_request_add(lambda o: elm.exit())
19 |
20 | self.fs = fs = FileSelector(win, size_hint_weight=EXPAND_BOTH, size_hint_align=FILL_BOTH)
21 | #fs.folderOnlySet(True)
22 | fs.setMode("Open")
23 | fs.show()
24 |
25 | fs.callback_activated_add(self.showFile)
26 | fs.callback_cancel_add(lambda o: elm.exit())
27 |
28 | win.resize_object_add(fs)
29 |
30 | win.resize(600, 400)
31 | win.show()
32 |
33 | def showFile(self, fs, ourFile):
34 | print(ourFile)
35 |
36 | if __name__ == "__main__":
37 | GUI = MainWindow()
38 | elm.run()
39 | GUI.fs.shutdown()
40 | elm.shutdown()
41 |
--------------------------------------------------------------------------------
/test_searchablelist.py:
--------------------------------------------------------------------------------
1 | import efl.elementary as elm
2 | from efl.elementary.window import StandardWindow
3 | elm.init()
4 | from efl.evas import EVAS_HINT_EXPAND, EVAS_HINT_FILL
5 |
6 | EXPAND_BOTH = EVAS_HINT_EXPAND, EVAS_HINT_EXPAND
7 | FILL_BOTH = EVAS_HINT_FILL, EVAS_HINT_FILL
8 |
9 | from elmextensions import SearchableList
10 |
11 | class MainWindow(object):
12 | def __init__( self ):
13 | win = StandardWindow("Testing", "Elementary SearchableList")
14 | win.callback_delete_request_add(lambda o: elm.exit())
15 |
16 | ourList = SearchableList(win, size_hint_weight=EXPAND_BOTH, size_hint_align=FILL_BOTH)
17 | self.keys = ["Jeff", "Kristi", "Jacob", "Declan", "Joris",
18 | "Timmy", "Tristam"]
19 | for kbl in self.keys:
20 | ourList.item_append(kbl)
21 | ourList.show()
22 |
23 | win.resize_object_add(ourList)
24 |
25 | win.resize(600, 400)
26 | win.show()
27 |
28 | if __name__ == "__main__":
29 | GUI = MainWindow()
30 | elm.run()
31 | elm.shutdown()
32 |
--------------------------------------------------------------------------------
/test_sortedlist.py:
--------------------------------------------------------------------------------
1 | #Used to generate some random numbers
2 | import random
3 |
4 | #Elementary stuff we want
5 | import efl.elementary as elm
6 | elm.init()
7 | from efl.elementary.window import StandardWindow
8 | from efl.elementary.label import Label
9 | from efl.elementary.button import Button
10 | from efl.elementary.separator import Separator
11 | from efl.elementary.box import Box
12 | from efl.evas import EVAS_HINT_EXPAND, EVAS_HINT_FILL
13 |
14 | #Our SortedList from elmextensions
15 | from elmextensions import SortedList
16 |
17 | #Fill commands
18 | EXPAND_BOTH = EVAS_HINT_EXPAND, EVAS_HINT_EXPAND
19 | FILL_BOTH = EVAS_HINT_FILL, EVAS_HINT_FILL
20 |
21 | #Defines how large our table generated will be
22 | ROWS = 50
23 | COLUMNS = 5
24 |
25 | class MainWindow(object):
26 | def __init__( self ):
27 | win = StandardWindow("Testing", "Elementary Sorted Table")
28 | win.callback_delete_request_add(lambda o: elm.exit())
29 |
30 | """Build the titles for the table. The titles is a list of tuples with the following format:
31 |
32 | ( , )"""
33 | titles = []
34 | for i in range(COLUMNS):
35 | titles.append(
36 | ("Column " + str(i), True if i != 2 else False)
37 | )
38 |
39 | #Create our sorted list object
40 | slist = SortedList(win, titles=titles, size_hint_weight=EXPAND_BOTH,
41 | size_hint_align=FILL_BOTH)
42 |
43 | #Populate the rows in our table
44 | for i in range(ROWS):
45 | #Each row is a list with the number of elements that must equal the number of headers
46 | row = []
47 | for j in range(COLUMNS):
48 | #Row elements can be ANY elementary object
49 | if j == 0:
50 | #For the first column in each row, we will create a button that will delete the row when pressed
51 | btn = Button(slist, size_hint_weight=EXPAND_BOTH,
52 | size_hint_align=FILL_BOTH)
53 | btn.text = "Delete row"
54 | btn.callback_clicked_add(
55 | lambda x, y=row: slist.row_unpack(y, delete=True)
56 | )
57 | btn.show()
58 | #Add the btn created to our row
59 | row.append(btn)
60 | else:
61 | #For each other row create a label with a random number
62 | data = random.randint(0, ROWS*COLUMNS)
63 | lb = Label(slist, size_hint_weight=EXPAND_BOTH,
64 | size_hint_align=FILL_BOTH)
65 | lb.text=str(data)
66 | """For integer data we also need to assign value to "sort_data" because otherwise things get sorted as text"""
67 | lb.data["sort_data"] = data
68 | lb.show()
69 | #Append our label to the row
70 | row.append(lb)
71 | #Add the row into the SortedList
72 | slist.row_pack(row, sort=False)
73 |
74 | #Show the list
75 | slist.show()
76 |
77 | win.resize_object_add(slist)
78 |
79 | win.resize(600, 400)
80 | win.show()
81 |
82 | if __name__ == "__main__":
83 | GUI = MainWindow()
84 | elm.run()
85 | elm.shutdown()
86 |
--------------------------------------------------------------------------------
/test_tabbedbox.py:
--------------------------------------------------------------------------------
1 | import efl.elementary as elm
2 | elm.init()
3 | from efl.elementary.window import StandardWindow
4 | from efl.elementary.label import Label
5 | from efl.evas import EVAS_HINT_EXPAND, EVAS_HINT_FILL
6 |
7 | from elmextensions import TabbedBox
8 |
9 | EXPAND_BOTH = EVAS_HINT_EXPAND, EVAS_HINT_EXPAND
10 | FILL_BOTH = EVAS_HINT_FILL, EVAS_HINT_FILL
11 |
12 | class MainWindow(object):
13 | def __init__( self ):
14 | win = StandardWindow("Testing", "Elementary Tabbed Widget")
15 | win.callback_delete_request_add(lambda o: elm.exit())
16 |
17 | tabbs = TabbedBox(win, size_hint_weight=EXPAND_BOTH, size_hint_align=FILL_BOTH)
18 | tabbs.closeCallback = self.closeChecks
19 |
20 | for i in range(10):
21 | lbl = Label(win)
22 | lbl.text = "Tab %s"%i
23 | lbl.show()
24 | tabbs.addTab(lbl, "Tab %s"%i)
25 |
26 | tabbs.disableTab(0)
27 | tabbs.disableTab(3)
28 |
29 | tabbs.show()
30 |
31 | win.resize_object_add(tabbs)
32 |
33 | win.resize(600, 400)
34 | win.show()
35 |
36 | def closeChecks(self, tabbs, widget):
37 | print widget.text
38 | if widget.text != "Tab 1":
39 | tabbs.deleteTab(widget)
40 |
41 | if __name__ == "__main__":
42 | GUI = MainWindow()
43 | elm.run()
44 | elm.shutdown()
45 |
--------------------------------------------------------------------------------