pytribeam 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pytribeam/GUI/CustomTkinterWidgets/__init__.py +28 -0
- pytribeam/GUI/CustomTkinterWidgets/button.py +312 -0
- pytribeam/GUI/CustomTkinterWidgets/config.py +20 -0
- pytribeam/GUI/CustomTkinterWidgets/datepicker.py +209 -0
- pytribeam/GUI/CustomTkinterWidgets/dialog.py +108 -0
- pytribeam/GUI/CustomTkinterWidgets/entry.py +182 -0
- pytribeam/GUI/CustomTkinterWidgets/frame.py +383 -0
- pytribeam/GUI/CustomTkinterWidgets/images.py +17 -0
- pytribeam/GUI/CustomTkinterWidgets/label.py +71 -0
- pytribeam/GUI/CustomTkinterWidgets/listbox.py +109 -0
- pytribeam/GUI/CustomTkinterWidgets/menu.py +113 -0
- pytribeam/GUI/CustomTkinterWidgets/menubutton.py +204 -0
- pytribeam/GUI/CustomTkinterWidgets/progressbar.py +560 -0
- pytribeam/GUI/CustomTkinterWidgets/scrollbar.py +228 -0
- pytribeam/GUI/CustomTkinterWidgets/text.py +192 -0
- pytribeam/GUI/CustomTkinterWidgets/theme.py +153 -0
- pytribeam/GUI/CustomTkinterWidgets/tooltip.py +200 -0
- pytribeam/GUI/CustomTkinterWidgets/utils.py +668 -0
- pytribeam/GUI/__init__.py +1 -0
- pytribeam/GUI/common/__init__.py +25 -0
- pytribeam/GUI/common/config_manager.py +172 -0
- pytribeam/GUI/common/errors.py +88 -0
- pytribeam/GUI/common/logging_config.py +128 -0
- pytribeam/GUI/common/resources.py +118 -0
- pytribeam/GUI/common/threading_utils.py +472 -0
- pytribeam/GUI/config_ui/App.py +1316 -0
- pytribeam/GUI/config_ui/__init__.py +1 -0
- pytribeam/GUI/config_ui/editor_controller.py +441 -0
- pytribeam/GUI/config_ui/lookup.py +1317 -0
- pytribeam/GUI/config_ui/microscope_interface.py +264 -0
- pytribeam/GUI/config_ui/parameter_tracker.py +278 -0
- pytribeam/GUI/config_ui/pipeline_model.py +755 -0
- pytribeam/GUI/config_ui/validator.py +348 -0
- pytribeam/GUI/runner.py +631 -0
- pytribeam/GUI/runner_util/__init__.py +5 -0
- pytribeam/GUI/runner_util/experiment_controller.py +521 -0
- pytribeam/GUI/runner_util/ui_components.py +540 -0
- pytribeam/__init__.py +19 -0
- pytribeam/command_line.py +153 -0
- pytribeam/constants.py +390 -0
- pytribeam/factory.py +3578 -0
- pytribeam/fib.py +534 -0
- pytribeam/image.py +1671 -0
- pytribeam/insertable_devices.py +819 -0
- pytribeam/laser.py +998 -0
- pytribeam/log.py +415 -0
- pytribeam/stage.py +960 -0
- pytribeam/types.py +2353 -0
- pytribeam/utilities.py +1043 -0
- pytribeam/workflow.py +837 -0
- pytribeam-0.0.1.dist-info/LICENSE +23 -0
- pytribeam-0.0.1.dist-info/METADATA +37 -0
- pytribeam-0.0.1.dist-info/NOTICE.md +917 -0
- pytribeam-0.0.1.dist-info/RECORD +57 -0
- pytribeam-0.0.1.dist-info/WHEEL +5 -0
- pytribeam-0.0.1.dist-info/entry_points.txt +5 -0
- pytribeam-0.0.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""
|
|
3
|
+
Code taken from https://github.com/Aboghazala/AwesomeTkinter/
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import tkinter as tk
|
|
7
|
+
from tkinter import ttk
|
|
8
|
+
|
|
9
|
+
from .button import Button, Button3d, Radiobutton, Checkbutton
|
|
10
|
+
from .frame import Frame3d, ScrollableFrame, ToggledFrame, ExpandableFrame
|
|
11
|
+
from .menu import RightClickMenu
|
|
12
|
+
from .progressbar import RadialProgressbar, RadialProgressbar3d, Segmentbar, Progressbar
|
|
13
|
+
from .scrollbar import SimpleScrollbar
|
|
14
|
+
from .text import ScrolledText
|
|
15
|
+
from .utils import *
|
|
16
|
+
from .config import *
|
|
17
|
+
from .theme import *
|
|
18
|
+
from .tooltip import tooltip
|
|
19
|
+
from .listbox import Listbox, DragDropListbox
|
|
20
|
+
from .menubutton import MenuButton, EntryMenuButton
|
|
21
|
+
from .entry import Entry, PathEntry
|
|
22
|
+
from .label import AutofitLabel, AutoWrappingLabel
|
|
23
|
+
from .dialog import filechooser, folderchooser
|
|
24
|
+
from .datepicker import DatePicker
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def main():
|
|
28
|
+
print("Hello from CustomTkinterWidgets!")
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Code taken from https://github.com/Aboghazala/AwesomeTkinter/
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import tkinter as tk
|
|
6
|
+
from tkinter import ttk
|
|
7
|
+
from .utils import *
|
|
8
|
+
from .config import *
|
|
9
|
+
from .images import *
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Button(tk.Button):
|
|
13
|
+
"""A tk MenuButton."""
|
|
14
|
+
|
|
15
|
+
def __init__(
|
|
16
|
+
self, parent, text, h_bg=None, h_fg=None, bg=None, fg=None, command=None, **kw
|
|
17
|
+
):
|
|
18
|
+
"""Initialize the menubutton."""
|
|
19
|
+
|
|
20
|
+
self.bg = bg or get_widget_attribute(parent, "background")
|
|
21
|
+
self.fg = fg or calc_font_color(self.bg)
|
|
22
|
+
h_bg = h_bg or bg
|
|
23
|
+
h_fg = h_fg or fg
|
|
24
|
+
a_fg = h_fg or fg
|
|
25
|
+
a_bg = h_bg or bg
|
|
26
|
+
kw.update(
|
|
27
|
+
dict(
|
|
28
|
+
text=text,
|
|
29
|
+
bg=self.bg,
|
|
30
|
+
fg=self.fg,
|
|
31
|
+
highlightcolor=h_bg,
|
|
32
|
+
activebackground=a_bg,
|
|
33
|
+
activeforeground=a_fg,
|
|
34
|
+
command=command,
|
|
35
|
+
)
|
|
36
|
+
)
|
|
37
|
+
tk.Button.__init__(self, parent, **kw)
|
|
38
|
+
self.bind("<Enter>", self.on_enter)
|
|
39
|
+
self.bind("<Leave>", self.on_leave)
|
|
40
|
+
|
|
41
|
+
def on_enter(self, e):
|
|
42
|
+
self.config(bg=self["activebackground"], fg=self["activeforeground"])
|
|
43
|
+
|
|
44
|
+
def on_leave(self, e):
|
|
45
|
+
self.config(bg=self.bg, fg=self.fg)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class Button3d(ttk.Button):
|
|
49
|
+
"""create a button with 3d background color and shadow"""
|
|
50
|
+
|
|
51
|
+
styles = []
|
|
52
|
+
|
|
53
|
+
def __init__(self, parent, bg=None, fg=None, **options):
|
|
54
|
+
"""initialize
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
parent: tkinter container widget, i.e. root or another frame
|
|
58
|
+
bg (str): button color
|
|
59
|
+
fg (str): text color
|
|
60
|
+
"""
|
|
61
|
+
self.bg = bg or DEFAULT_COLOR
|
|
62
|
+
parent_color = get_widget_attribute(parent, "background") or DEFAULT_COLOR
|
|
63
|
+
|
|
64
|
+
# initialize super class
|
|
65
|
+
ttk.Button.__init__(self, parent, **options)
|
|
66
|
+
|
|
67
|
+
# create unique style name based on frame color
|
|
68
|
+
button_style = f"Button3d_{len(Button3d.styles)}_.TButton"
|
|
69
|
+
|
|
70
|
+
# create style
|
|
71
|
+
if button_style not in Button3d.styles:
|
|
72
|
+
self.create_images()
|
|
73
|
+
|
|
74
|
+
# create elements
|
|
75
|
+
s = ttk.Style()
|
|
76
|
+
element_name = f"Button.focus.{button_style}_element"
|
|
77
|
+
s.element_create(
|
|
78
|
+
element_name,
|
|
79
|
+
"image",
|
|
80
|
+
self.img,
|
|
81
|
+
("pressed", self.pressed_img),
|
|
82
|
+
border=12,
|
|
83
|
+
sticky="nsew",
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
s.layout(
|
|
87
|
+
button_style,
|
|
88
|
+
[
|
|
89
|
+
(
|
|
90
|
+
element_name,
|
|
91
|
+
{
|
|
92
|
+
"sticky": "nswe",
|
|
93
|
+
"children": [
|
|
94
|
+
(
|
|
95
|
+
"Button.padding",
|
|
96
|
+
{
|
|
97
|
+
"sticky": "nswe",
|
|
98
|
+
"children": [
|
|
99
|
+
("Button.label", {"sticky": "nswe"})
|
|
100
|
+
],
|
|
101
|
+
},
|
|
102
|
+
)
|
|
103
|
+
],
|
|
104
|
+
},
|
|
105
|
+
)
|
|
106
|
+
],
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
s.map(
|
|
110
|
+
button_style,
|
|
111
|
+
background=[("", parent_color)],
|
|
112
|
+
foreground=[("", calc_font_color(self.bg))],
|
|
113
|
+
)
|
|
114
|
+
s.configure(button_style, padding=0, borderwidth=0, focuscolor=self.bg)
|
|
115
|
+
s.configure(button_style, font=FONT)
|
|
116
|
+
|
|
117
|
+
# add to styles
|
|
118
|
+
Button3d.styles.append(button_style)
|
|
119
|
+
|
|
120
|
+
self["style"] = button_style
|
|
121
|
+
|
|
122
|
+
def create_images(self):
|
|
123
|
+
|
|
124
|
+
shadow_img = create_pil_image(b64=btn_base)
|
|
125
|
+
img = create_pil_image(b64=btn_face, color=self.bg)
|
|
126
|
+
|
|
127
|
+
# merge face with base image
|
|
128
|
+
img = mix_images(shadow_img, img)
|
|
129
|
+
|
|
130
|
+
pressed_img = img.rotate(180)
|
|
131
|
+
|
|
132
|
+
self.img = ImageTk.PhotoImage(img)
|
|
133
|
+
self.pressed_img = ImageTk.PhotoImage(pressed_img)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
class Radiobutton(ttk.Radiobutton):
|
|
137
|
+
"""ttk.Radiobutton with better indicator quality"""
|
|
138
|
+
|
|
139
|
+
styles = []
|
|
140
|
+
imgs = {"outline_img": {}, "selection_img": {}}
|
|
141
|
+
|
|
142
|
+
def __init__(
|
|
143
|
+
self,
|
|
144
|
+
parent,
|
|
145
|
+
text=None,
|
|
146
|
+
ind_bg=None,
|
|
147
|
+
ind_mark_color=None,
|
|
148
|
+
ind_outline_color=None,
|
|
149
|
+
bg=None,
|
|
150
|
+
fg=None,
|
|
151
|
+
font=None,
|
|
152
|
+
value=None,
|
|
153
|
+
**kwargs,
|
|
154
|
+
):
|
|
155
|
+
"""initialize
|
|
156
|
+
Args:
|
|
157
|
+
parent: tkinter container
|
|
158
|
+
text (str): text
|
|
159
|
+
ind_bg (str): indicator ring background "fill color"
|
|
160
|
+
ind_outline_color (str): indicator outline / ring color
|
|
161
|
+
ind_mark_color (str): check mark color
|
|
162
|
+
bg (str): background color "should match parent bg
|
|
163
|
+
fg (str): text color
|
|
164
|
+
font (str): text font, e.g. "any 10 bold"
|
|
165
|
+
value (any): value assigned to button variable when selected
|
|
166
|
+
"""
|
|
167
|
+
bg = bg or get_widget_attribute(parent, "background")
|
|
168
|
+
fg = fg or calc_font_color(bg)
|
|
169
|
+
ind_bg = ind_bg or bg
|
|
170
|
+
ind_outline_color = ind_outline_color or fg
|
|
171
|
+
ind_mark_color = ind_mark_color or fg
|
|
172
|
+
|
|
173
|
+
value = value if value is not None else text
|
|
174
|
+
|
|
175
|
+
custom_style = f"RadioButton_{len(Radiobutton.styles)}"
|
|
176
|
+
Radiobutton.styles.append(custom_style)
|
|
177
|
+
|
|
178
|
+
s = ttk.Style()
|
|
179
|
+
|
|
180
|
+
self.outline_img = Radiobutton.imgs["outline_img"].get(ind_outline_color)
|
|
181
|
+
self.selection_img = Radiobutton.imgs["selection_img"].get(ind_mark_color)
|
|
182
|
+
|
|
183
|
+
# create tkinter PhotoImage
|
|
184
|
+
if not (self.outline_img and self.selection_img):
|
|
185
|
+
# create indicator outline
|
|
186
|
+
outline_img = create_circle(
|
|
187
|
+
size=12, thickness=1, color=ind_outline_color, fill=ind_bg, offset=2
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
# create indicator mark
|
|
191
|
+
mark_img = create_circle(
|
|
192
|
+
size=6, thickness=0, color=ind_mark_color, fill=ind_mark_color
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
selection_img = mix_images(outline_img, mark_img)
|
|
196
|
+
|
|
197
|
+
# create tkinter PhotoImage
|
|
198
|
+
self.outline_img = create_image(img=outline_img)
|
|
199
|
+
self.selection_img = create_image(img=selection_img)
|
|
200
|
+
|
|
201
|
+
# store them for future use
|
|
202
|
+
Radiobutton.imgs["outline_img"][ind_outline_color] = self.outline_img
|
|
203
|
+
Radiobutton.imgs["selection_img"][ind_mark_color] = self.selection_img
|
|
204
|
+
|
|
205
|
+
# custom style
|
|
206
|
+
s.layout(
|
|
207
|
+
custom_style,
|
|
208
|
+
[
|
|
209
|
+
(
|
|
210
|
+
"Radiobutton.padding",
|
|
211
|
+
{
|
|
212
|
+
"sticky": "nswe",
|
|
213
|
+
"children": [("Radiobutton.label", {"sticky": "nswe"})],
|
|
214
|
+
},
|
|
215
|
+
)
|
|
216
|
+
],
|
|
217
|
+
)
|
|
218
|
+
s.configure(custom_style, foreground=fg)
|
|
219
|
+
s.map(custom_style, background=[("", bg)])
|
|
220
|
+
|
|
221
|
+
if font:
|
|
222
|
+
s.configure(custom_style, font=font)
|
|
223
|
+
|
|
224
|
+
# set default options
|
|
225
|
+
options = dict(
|
|
226
|
+
text=text,
|
|
227
|
+
value=value,
|
|
228
|
+
style=custom_style,
|
|
229
|
+
image=[self.outline_img, "selected", self.selection_img],
|
|
230
|
+
compound="left",
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
# override default options
|
|
234
|
+
options.update(**kwargs)
|
|
235
|
+
|
|
236
|
+
# initialize super class
|
|
237
|
+
ttk.Radiobutton.__init__(self, master=parent, **options)
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
class Checkbutton(tk.Checkbutton):
|
|
241
|
+
"""tk.checkbutton with images"""
|
|
242
|
+
|
|
243
|
+
imgs = {"empty_box_img": {}, "checked_box_img": {}}
|
|
244
|
+
|
|
245
|
+
def __init__(
|
|
246
|
+
self,
|
|
247
|
+
parent,
|
|
248
|
+
box_color=None,
|
|
249
|
+
check_mark_color=None,
|
|
250
|
+
text_color=None,
|
|
251
|
+
size=None,
|
|
252
|
+
**kwargs,
|
|
253
|
+
):
|
|
254
|
+
"""initialize
|
|
255
|
+
Args:
|
|
256
|
+
parent: tkinter container
|
|
257
|
+
box_color (str): checkbox outline color
|
|
258
|
+
check_mark_color (str): check mark color
|
|
259
|
+
"""
|
|
260
|
+
bg = kwargs.get("bg") or get_widget_attribute(
|
|
261
|
+
parent, "background"
|
|
262
|
+
) # bg of checkbox
|
|
263
|
+
check_mark_color = check_mark_color or calc_font_color(bg)
|
|
264
|
+
box_color = box_color or calc_font_color(bg)
|
|
265
|
+
text_color = text_color or calc_font_color(bg)
|
|
266
|
+
|
|
267
|
+
# images
|
|
268
|
+
self.empty_box_img = Checkbutton.imgs["empty_box_img"].get(box_color)
|
|
269
|
+
self.checked_box_img = Checkbutton.imgs["checked_box_img"].get(check_mark_color)
|
|
270
|
+
|
|
271
|
+
# create tkinter PhotoImage
|
|
272
|
+
if not (self.empty_box_img and self.checked_box_img):
|
|
273
|
+
self.empty_box_img = create_image(
|
|
274
|
+
b64=unchecked_icon, color=box_color, size=size
|
|
275
|
+
)
|
|
276
|
+
self.checked_box_img = create_image(
|
|
277
|
+
b64=checked_icon, color=check_mark_color, size=size
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
# store images for future use
|
|
281
|
+
Checkbutton.imgs["empty_box_img"][box_color] = self.empty_box_img
|
|
282
|
+
Checkbutton.imgs["checked_box_img"][check_mark_color] = self.checked_box_img
|
|
283
|
+
|
|
284
|
+
# set default options
|
|
285
|
+
options = dict(
|
|
286
|
+
bg=bg,
|
|
287
|
+
activebackground=bg,
|
|
288
|
+
highlightbackground=bg,
|
|
289
|
+
fg=text_color,
|
|
290
|
+
activeforeground=text_color,
|
|
291
|
+
selectcolor=bg,
|
|
292
|
+
compound="left",
|
|
293
|
+
indicatoron=False, # disable original indicator
|
|
294
|
+
image=self.empty_box_img,
|
|
295
|
+
selectimage=self.checked_box_img,
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
# add space before text
|
|
299
|
+
text = kwargs.get("text")
|
|
300
|
+
if text:
|
|
301
|
+
kwargs["text"] = " " + text
|
|
302
|
+
|
|
303
|
+
# override default options
|
|
304
|
+
options.update(**kwargs)
|
|
305
|
+
if "dtype" in kwargs:
|
|
306
|
+
options.pop("dtype")
|
|
307
|
+
|
|
308
|
+
# initialize super class
|
|
309
|
+
tk.Checkbutton.__init__(self, master=parent, **options)
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
__all__ = ["Button", "Button3d", "Radiobutton", "Checkbutton"]
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# DEFAULT_COLOR = '#333'
|
|
2
|
+
# ACCENT_COLOR1 = '#00baff'
|
|
3
|
+
# ACCENT_COLOR2 = '#ff00ff'
|
|
4
|
+
DEFAULT_COLOR = "#eeeeee"
|
|
5
|
+
ACCENT_COLOR1 = "#e7e614"
|
|
6
|
+
ACCENT_COLOR2 = "#92278f"
|
|
7
|
+
ACCENT_COLOR3 = "#ed1c24"
|
|
8
|
+
GREEN = "#2d9546"
|
|
9
|
+
RED = "#ed1c24"
|
|
10
|
+
YELLOW = "#f7e615"
|
|
11
|
+
BLACK = "#000"
|
|
12
|
+
TERMINAL_COLOR = "#333"
|
|
13
|
+
|
|
14
|
+
HEADER_FONT = ("Segoe UI", 15, "bold")
|
|
15
|
+
SUBHEADER_FONT = ("Segoe UI", 13, "bold")
|
|
16
|
+
FONT = ("Segoe UI", 11)
|
|
17
|
+
FONT_BOLD = ("Segoe UI", 11, "bold")
|
|
18
|
+
FONT_ITALIC = ("Segoe UI", 11, "italic")
|
|
19
|
+
MENU_FONT = ("Segoe UI", 9)
|
|
20
|
+
TIP_FONT = ("Segoe UI", 9)
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Code taken from https://github.com/Aboghazala/AwesomeTkinter/
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import datetime
|
|
6
|
+
import tkinter as tk
|
|
7
|
+
from tkinter import ttk
|
|
8
|
+
|
|
9
|
+
from .utils import *
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class DatePicker(tk.Toplevel):
|
|
13
|
+
"""Date picker window"""
|
|
14
|
+
|
|
15
|
+
def __init__(
|
|
16
|
+
self,
|
|
17
|
+
master,
|
|
18
|
+
min_year=None,
|
|
19
|
+
max_year=None,
|
|
20
|
+
year=None,
|
|
21
|
+
month=None,
|
|
22
|
+
day=None,
|
|
23
|
+
hour=None,
|
|
24
|
+
minute=None,
|
|
25
|
+
title="Date Picker",
|
|
26
|
+
bg=None,
|
|
27
|
+
fg=None,
|
|
28
|
+
sbg=None,
|
|
29
|
+
btnbg=None,
|
|
30
|
+
btnfg=None,
|
|
31
|
+
width=420,
|
|
32
|
+
height=180,
|
|
33
|
+
):
|
|
34
|
+
"""initialize
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
parent: parent window
|
|
38
|
+
min_year (int): minimum year to show
|
|
39
|
+
max_year (int): max. year to show
|
|
40
|
+
year, month, day, hour, minute (int): set selected time
|
|
41
|
+
title (str): window title
|
|
42
|
+
"""
|
|
43
|
+
self.master = master
|
|
44
|
+
master_bg = get_widget_attribute(master, "background")
|
|
45
|
+
self.bg = bg or calc_contrast_color(master_bg, 30)
|
|
46
|
+
self.fg = fg or calc_font_color(self.bg)
|
|
47
|
+
self.sbg = sbg or fg
|
|
48
|
+
self.btnbg = btnbg or self.sbg
|
|
49
|
+
self.btnfg = btnfg or calc_font_color(self.btnbg)
|
|
50
|
+
|
|
51
|
+
today = datetime.datetime.today()
|
|
52
|
+
year = year or today.year
|
|
53
|
+
min_year = min_year or year - 100
|
|
54
|
+
max_year = max_year or year + 100
|
|
55
|
+
month = month or today.month
|
|
56
|
+
day = day or today.day
|
|
57
|
+
hour = hour or today.hour
|
|
58
|
+
minute = minute or today.minute
|
|
59
|
+
|
|
60
|
+
self.selected_date = None
|
|
61
|
+
|
|
62
|
+
self.fields = {
|
|
63
|
+
"Year": {"values": list(range(min_year, max_year + 1)), "selection": year},
|
|
64
|
+
"Month": {"values": list(range(1, 13)), "selection": month},
|
|
65
|
+
"Day": {"values": list(range(1, 32)), "selection": day},
|
|
66
|
+
"Hour": {"values": list(range(0, 24)), "selection": hour},
|
|
67
|
+
"Minute": {"values": list(range(0, 60)), "selection": minute},
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
# initialize super
|
|
71
|
+
tk.Toplevel.__init__(self, self.master)
|
|
72
|
+
|
|
73
|
+
# bind window close
|
|
74
|
+
center_window(self, width=width, height=height, reference=self.master)
|
|
75
|
+
|
|
76
|
+
self.title(title)
|
|
77
|
+
self.config(bg=self.sbg)
|
|
78
|
+
|
|
79
|
+
self.create_widgets()
|
|
80
|
+
|
|
81
|
+
self.wait_window(self)
|
|
82
|
+
|
|
83
|
+
def is_leap(self, year):
|
|
84
|
+
"""year -> 1 if leap year, else 0, source: datetime.py"""
|
|
85
|
+
return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)
|
|
86
|
+
|
|
87
|
+
def days_in_month(self, year, month):
|
|
88
|
+
"""year, month -> number of days in that month in that year, modified from source: datetime.py"""
|
|
89
|
+
default = [-1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
|
|
90
|
+
|
|
91
|
+
if month == 2 and self.is_leap(year):
|
|
92
|
+
return 29
|
|
93
|
+
return default[month]
|
|
94
|
+
|
|
95
|
+
def create_widgets(self):
|
|
96
|
+
bg = self.bg
|
|
97
|
+
fg = self.fg
|
|
98
|
+
sbg = self.sbg
|
|
99
|
+
btnbg = self.btnbg
|
|
100
|
+
btnfg = self.btnfg
|
|
101
|
+
main_frame = tk.Frame(self, bg=bg)
|
|
102
|
+
top_frame = tk.Frame(main_frame, bg=bg)
|
|
103
|
+
tk.Label(top_frame, text="Select Date and time:", bg=bg, fg=fg).pack(
|
|
104
|
+
side="left", padx=5, pady=5
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
middle_frame = tk.Frame(main_frame, bg=bg)
|
|
108
|
+
|
|
109
|
+
def set_field(field_name, value):
|
|
110
|
+
x = self.fields[field_name]
|
|
111
|
+
x["selection"] = int(value)
|
|
112
|
+
|
|
113
|
+
# set correct days according to month and year
|
|
114
|
+
year = self.fields["Year"]["selection"]
|
|
115
|
+
month = self.fields["Month"]["selection"]
|
|
116
|
+
day = self.fields["Day"]["selection"]
|
|
117
|
+
days_in_month = self.days_in_month(year, month)
|
|
118
|
+
day_combo.config(values=list(range(1, days_in_month + 1)))
|
|
119
|
+
if day > days_in_month:
|
|
120
|
+
corrected_value = days_in_month
|
|
121
|
+
day_combo.set(corrected_value)
|
|
122
|
+
self.fields["Day"]["selection"] = corrected_value
|
|
123
|
+
|
|
124
|
+
# style
|
|
125
|
+
s = ttk.Style()
|
|
126
|
+
custom_style = "custom.TCombobox"
|
|
127
|
+
# combobox consists of a text area, down arrow, and dropdown menu (listbox)
|
|
128
|
+
# arrow: arrowcolor, background
|
|
129
|
+
# text area: foreground, fieldbackground
|
|
130
|
+
arrow_bg = sbg
|
|
131
|
+
textarea_bg = btnbg
|
|
132
|
+
s.configure(
|
|
133
|
+
custom_style,
|
|
134
|
+
arrowcolor=calc_font_color(arrow_bg),
|
|
135
|
+
foreground=calc_font_color(textarea_bg),
|
|
136
|
+
padding=4,
|
|
137
|
+
relief=tk.RAISED,
|
|
138
|
+
)
|
|
139
|
+
s.map(
|
|
140
|
+
custom_style,
|
|
141
|
+
fieldbackground=[("", textarea_bg)],
|
|
142
|
+
background=[("", arrow_bg)],
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
def on_selection(event):
|
|
146
|
+
widget = event.widget
|
|
147
|
+
widget.selection_clear()
|
|
148
|
+
|
|
149
|
+
widget.callback()
|
|
150
|
+
|
|
151
|
+
c = 0
|
|
152
|
+
for key, item in self.fields.items():
|
|
153
|
+
tk.Label(middle_frame, text=key, bg=bg, fg=fg).grid(
|
|
154
|
+
row=0, column=c, padx=5, sticky="w"
|
|
155
|
+
)
|
|
156
|
+
cb = ttk.Combobox(
|
|
157
|
+
middle_frame,
|
|
158
|
+
state="readonly",
|
|
159
|
+
values=item["values"],
|
|
160
|
+
style=custom_style,
|
|
161
|
+
width=5,
|
|
162
|
+
)
|
|
163
|
+
cb.set(item["selection"])
|
|
164
|
+
cb.grid(row=1, column=c, padx=(5, 10), sticky="w")
|
|
165
|
+
cb.callback = lambda field_name=key, combo=cb: set_field(
|
|
166
|
+
field_name, combo.get()
|
|
167
|
+
)
|
|
168
|
+
cb.bind("<<ComboboxSelected>>", on_selection)
|
|
169
|
+
|
|
170
|
+
# get day combo box reference
|
|
171
|
+
if key == "Day":
|
|
172
|
+
day_combo = cb
|
|
173
|
+
|
|
174
|
+
c += 1
|
|
175
|
+
|
|
176
|
+
bottom_frame = tk.Frame(main_frame, bg=bg)
|
|
177
|
+
tk.Button(
|
|
178
|
+
bottom_frame, text="Cancel", command=self.close, bg=btnbg, fg=btnfg
|
|
179
|
+
).pack(side="right", padx=5)
|
|
180
|
+
tk.Button(
|
|
181
|
+
bottom_frame, text="Ok", command=self.set_selection, bg=btnbg, fg=btnfg
|
|
182
|
+
).pack(side="right")
|
|
183
|
+
|
|
184
|
+
main_frame.pack(expand=True, fill="both", padx=(10, 0), pady=(10, 0))
|
|
185
|
+
bottom_frame.pack(side="bottom", fill="x", pady=5)
|
|
186
|
+
ttk.Separator(main_frame).pack(side="bottom", fill="x", expand=True)
|
|
187
|
+
middle_frame.pack(side="bottom", expand=True, fill="both")
|
|
188
|
+
ttk.Separator(main_frame).pack(side="bottom", fill="x", expand=True)
|
|
189
|
+
top_frame.pack(side="bottom", fill="x")
|
|
190
|
+
|
|
191
|
+
def close(self):
|
|
192
|
+
self.destroy()
|
|
193
|
+
|
|
194
|
+
def set_selection(self):
|
|
195
|
+
"""return selected date"""
|
|
196
|
+
values = [int(item["selection"]) for item in self.fields.values()]
|
|
197
|
+
self.selected_date = datetime.datetime(*values)
|
|
198
|
+
self.close()
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
if __name__ == "__main__":
|
|
202
|
+
root = tk.Tk()
|
|
203
|
+
|
|
204
|
+
def foo():
|
|
205
|
+
dp = DatePicker(root, year=1900, day=23, hour=18, bg="white", fg="black")
|
|
206
|
+
print(dp.selected_date)
|
|
207
|
+
|
|
208
|
+
tk.Button(root, text="select date", command=foo).pack()
|
|
209
|
+
root.mainloop()
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Code taken from https://github.com/Aboghazala/AwesomeTkinter/
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import platform
|
|
6
|
+
import subprocess
|
|
7
|
+
import shlex
|
|
8
|
+
from tkinter import filedialog
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
operating_system = (
|
|
12
|
+
platform.system()
|
|
13
|
+
) # current operating system ('Windows', 'Linux', 'Darwin')
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def run_command(cmd):
|
|
17
|
+
try:
|
|
18
|
+
p = subprocess.run(shlex.split(cmd), stdout=subprocess.PIPE, check=True)
|
|
19
|
+
return p.returncode, p.stdout.decode("utf-8").strip()
|
|
20
|
+
except Exception as e:
|
|
21
|
+
# print(e)
|
|
22
|
+
return -1, ""
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class FileDialog:
|
|
26
|
+
"""use alternative file chooser to replace tkinter ugly file chooser on linux
|
|
27
|
+
|
|
28
|
+
available options:
|
|
29
|
+
Zenity: command line tool, based on gtk
|
|
30
|
+
kdialog: command line tool for kde file chooser
|
|
31
|
+
gtk: use Gtk.FileChooserDialog directly thru python, "Don't not use", since sometimes it will raise
|
|
32
|
+
error: Gdk-Message: Fatal IO error 0 (Success) on X server :1, and application will crash
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
def __init__(self, foldersonly=False):
|
|
36
|
+
self.use = "TK" # , 'zenity', or 'kdialog'
|
|
37
|
+
self.foldersonly = foldersonly
|
|
38
|
+
self.title = "FireDM - "
|
|
39
|
+
self.title += "Select a folder" if self.foldersonly else "Select a file"
|
|
40
|
+
if operating_system == "Linux":
|
|
41
|
+
# looking for zenity
|
|
42
|
+
_, zenity_path = run_command("which zenity")
|
|
43
|
+
if zenity_path:
|
|
44
|
+
self.use = "zenity"
|
|
45
|
+
else:
|
|
46
|
+
# looking for kdialog
|
|
47
|
+
_, kdialog_path = run_command("which kdialog")
|
|
48
|
+
if kdialog_path:
|
|
49
|
+
self.use = "kdialog"
|
|
50
|
+
|
|
51
|
+
def run(self, initialdir="", backend=None):
|
|
52
|
+
selected_path = initialdir
|
|
53
|
+
if backend:
|
|
54
|
+
self.use = backend
|
|
55
|
+
|
|
56
|
+
if self.use == "zenity":
|
|
57
|
+
cmd = "zenity --file-selection"
|
|
58
|
+
if self.foldersonly:
|
|
59
|
+
cmd += " --directory"
|
|
60
|
+
if isinstance(initialdir, str):
|
|
61
|
+
cmd += f' --filename="{initialdir}"'
|
|
62
|
+
retcode, path = run_command(cmd)
|
|
63
|
+
# zenity will return either 0, 1 or 5, depending on whether the user pressed OK,
|
|
64
|
+
# Cancel or timeout has been reached
|
|
65
|
+
if retcode in (0, 1, 5):
|
|
66
|
+
selected_path = path
|
|
67
|
+
|
|
68
|
+
elif self.use == "kdialog":
|
|
69
|
+
cmd = "kdialog"
|
|
70
|
+
if self.foldersonly:
|
|
71
|
+
cmd += " --getexistingdirectory"
|
|
72
|
+
else:
|
|
73
|
+
cmd += " --getopenfilename"
|
|
74
|
+
|
|
75
|
+
if isinstance(initialdir, str):
|
|
76
|
+
cmd += f' "{initialdir}"'
|
|
77
|
+
|
|
78
|
+
retcode, path = run_command(cmd)
|
|
79
|
+
# kdialog will return either 0, 1 depending on whether the user pressed OK, Cancel
|
|
80
|
+
if retcode in (0, 1):
|
|
81
|
+
selected_path = path
|
|
82
|
+
else:
|
|
83
|
+
selected_path = self.run_default(initialdir=initialdir)
|
|
84
|
+
|
|
85
|
+
return selected_path
|
|
86
|
+
|
|
87
|
+
def run_default(self, initialdir=""):
|
|
88
|
+
# use ugly tkinter filechooser
|
|
89
|
+
if self.foldersonly:
|
|
90
|
+
selected_path = filedialog.askdirectory(initialdir=initialdir)
|
|
91
|
+
else:
|
|
92
|
+
selected_path = filedialog.askopenfilename(initialdir=initialdir)
|
|
93
|
+
|
|
94
|
+
return selected_path
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
filechooser = FileDialog().run
|
|
98
|
+
folderchooser = FileDialog(foldersonly=True).run
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
if __name__ == "__main__":
|
|
102
|
+
# ret, stdout = run_command('blah blah')
|
|
103
|
+
# print(ret, stdout)
|
|
104
|
+
test_fp = filechooser(backend="kdialog")
|
|
105
|
+
print(test_fp)
|
|
106
|
+
#
|
|
107
|
+
test_dir = folderchooser()
|
|
108
|
+
print(test_dir)
|