Skip to content
GitLab
Explore
Sign in
Register
Primary navigation
Search or go to…
Project
Alchemy Viewer
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Iterations
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Locked files
Deploy
Releases
Package registry
Operate
Terraform modules
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Terms and privacy
Keyboard shortcuts
?
Snippets
Groups
Projects
Silent mode is enabled
All outbound communications are blocked.
Learn more
.
Show more breadcrumbs
Alchemy Viewer
Alchemy Viewer
Commits
8bee0a61
Commit
8bee0a61
authored
8 years ago
by
Glenn Glazer
Browse files
Options
Downloads
Patches
Plain Diff
SL-407: create Tkinter UI
parent
c2ef3b4c
No related branches found
No related tags found
No related merge requests found
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
indra/viewer_components/manager/InstallerUserMessage.py
+258
-0
258 additions, 0 deletions
indra/viewer_components/manager/InstallerUserMessage.py
with
258 additions
and
0 deletions
indra/viewer_components/manager/InstallerUserMessage.py
0 → 100644
+
258
−
0
View file @
8bee0a61
#!/usr/bin/env python
# $LicenseInfo:firstyear=2016&license=internal$
#
# Copyright (c) 2016, Linden Research, Inc.
#
# The following source code is PROPRIETARY AND CONFIDENTIAL. Use of
# this source code is governed by the Linden Lab Source Code Disclosure
# Agreement ("Agreement") previously entered between you and Linden
# Lab. By accessing, using, copying, modifying or distributing this
# software, you acknowledge that you have been informed of your
# obligations under the Agreement and agree to abide by those obligations.
#
# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
# COMPLETENESS OR PERFORMANCE.
# $LicenseInfo:firstyear=2013&license=viewerlgpl$
# Copyright (c) 2013, Linden Research, Inc.
# $/LicenseInfo$
"""
@file InstallerUserMessage.py
@author coyot
@date 2016-05-16
"""
"""
This does everything the old updater/scripts/darwin/messageframe.py script did and some more bits.
Pushed up the manager directory to be multiplatform.
"""
import
os
import
Queue
import
threading
import
time
import
Tkinter
as
tk
import
ttk
class
InstallerUserMessage
(
tk
.
Tk
):
#Goals for this class:
# Provide a uniform look and feel
# Provide an easy to use convenience class for other scripts
# Provide windows that automatically disappear when done (for differing notions of done)
# Provide a progress bar that isn't a glorified spinner, but based on download progress
#Non-goals:
# No claim to threadsafety is made or warranted. Your mileage may vary.
# Please consult a doctor if you experience thread pain.
def
__init__
(
self
,
text
=
""
,
title
=
""
,
width
=
500
,
height
=
200
,
fillcolor
=
'
#487A7B
'
,
*
args
,
**
kwargs
):
tk
.
Tk
.
__init__
(
self
)
self
.
grid
()
self
.
title
(
title
)
self
.
choice
=
tk
.
BooleanVar
()
self
.
config
(
background
=
'
black
'
)
# background="..." doesn't work on MacOS for radiobuttons or progress bars
# http://tinyurl.com/tkmacbuttons
ttk
.
Style
().
configure
(
'
Linden.TLabel
'
,
foreground
=
'
#487A7B
'
,
background
=
'
black
'
)
ttk
.
Style
().
configure
(
'
Linden.TButton
'
,
foreground
=
'
#487A7B
'
,
background
=
'
black
'
)
ttk
.
Style
().
configure
(
"
black.Horizontal.TProgressbar
"
,
foreground
=
'
#487A7B
'
,
background
=
'
black
'
)
# The constants below are to adjust for typical overhead from the
# frame borders.
self
.
xp
=
(
self
.
winfo_screenwidth
()
/
2
)
-
(
width
/
2
)
-
8
self
.
yp
=
(
self
.
winfo_screenheight
()
/
2
)
-
(
height
/
2
)
-
20
self
.
geometry
(
'
{0}x{1}+{2}+{3}
'
.
format
(
width
,
height
,
self
.
xp
,
self
.
yp
))
self
.
script_dir
=
os
.
path
.
dirname
(
os
.
path
.
realpath
(
__file__
))
self
.
icon_dir
=
os
.
path
.
abspath
(
os
.
path
.
join
(
self
.
script_dir
,
'
icons
'
))
#defines what to do when window is closed
self
.
protocol
(
"
WM_DELETE_WINDOW
"
,
self
.
_delete_window
)
def
_delete_window
(
self
):
#capture and discard all destroy events before the choice is set
if
not
((
self
.
choice
==
None
)
or
(
self
.
choice
==
""
)):
try
:
self
.
destroy
()
except
:
#tk may try to destroy the same object twice
pass
def
set_colors
(
self
,
widget
):
# #487A7B is "Linden Green"
widget
.
config
(
foreground
=
'
#487A7B
'
)
widget
.
config
(
background
=
'
black
'
)
def
find_icon
(
self
,
icon_path
=
None
,
icon_name
=
None
):
#we do this in each message, let's do it just once instead.
if
not
icon_path
:
icon_path
=
self
.
icon_dir
icon_path
=
os
.
path
.
join
(
icon_path
,
icon_name
)
if
os
.
path
.
exists
(
icon_path
):
icon
=
tk
.
PhotoImage
(
file
=
icon_path
)
self
.
image_label
=
tk
.
Label
(
image
=
icon
)
self
.
image_label
.
image
=
icon
else
:
#default to text if image not available
self
.
image_label
=
tk
.
Label
(
text
=
"
Second Life
"
)
def
auto_resize
(
self
,
row_count
=
0
,
column_count
=
0
,
heavy_row
=
None
,
heavy_column
=
None
):
#auto resize window to fit all rows and columns
#"heavy" gets extra weight
for
x
in
range
(
column_count
):
if
x
==
heavy_column
:
self
.
columnconfigure
(
x
,
weight
=
2
)
else
:
self
.
columnconfigure
(
x
,
weight
=
1
)
for
y
in
range
(
row_count
):
if
y
==
heavy_row
:
self
.
rowconfigure
(
y
,
weight
=
2
)
else
:
self
.
rowconfigure
(
x
,
weight
=
1
)
def
basic_message
(
self
,
message
,
icon_path
=
None
,
icon_name
=
None
):
#message: text to be displayed
#icon_path: directory holding the icon, defaults to icons subdir of script dir
#icon_name: filename of icon to be displayed
self
.
choice
.
set
(
True
)
self
.
find_icon
(
icon_path
,
icon_name
)
self
.
text_label
=
tk
.
Label
(
text
=
message
)
self
.
set_colors
(
self
.
text_label
)
self
.
set_colors
(
self
.
image_label
)
#pad, direction and weight are all experimentally derived by retrying various values
self
.
image_label
.
grid
(
row
=
1
,
column
=
1
,
sticky
=
'
W
'
)
self
.
text_label
.
grid
(
row
=
1
,
column
=
2
,
sticky
=
'
W
'
,
padx
=
100
)
self
.
auto_resize
(
1
,
2
)
self
.
mainloop
()
def
binary_choice_message
(
self
,
message
,
icon_path
=
None
,
icon_name
=
None
,
one
=
'
Yes
'
,
two
=
'
No
'
):
#one: first option, returns True
#two: second option, returns False
#usage is kind of opaque and relies on this object persisting after the window destruction to pass back choice
#usage:
# frame = InstallerUserMessage.InstallerUserMessage( ... )
# frame = frame.binary_choice_message( ... )
# (wait for user to click)
# value = frame.choice.get()
self
.
find_icon
(
icon_path
,
icon_name
)
self
.
text_label
=
tk
.
Label
(
text
=
message
)
#command registers the callback to the method named. We want the frame to go away once clicked.
#button 1 returns True/1, button 2 returns False/0
self
.
button_one
=
ttk
.
Radiobutton
(
text
=
one
,
variable
=
self
.
choice
,
value
=
True
,
command
=
self
.
_delete_window
,
style
=
'
Linden.TButton
'
)
self
.
button_two
=
ttk
.
Radiobutton
(
text
=
two
,
variable
=
self
.
choice
,
value
=
False
,
command
=
self
.
_delete_window
,
style
=
'
Linden.TButton
'
)
self
.
set_colors
(
self
.
text_label
)
self
.
set_colors
(
self
.
image_label
)
#pad, direction and weight are all experimentally derived by retrying various values
self
.
image_label
.
grid
(
row
=
1
,
column
=
1
,
rowspan
=
3
,
sticky
=
'
W
'
)
self
.
text_label
.
grid
(
row
=
1
,
column
=
2
,
rowspan
=
3
)
self
.
button_one
.
grid
(
row
=
1
,
column
=
3
,
sticky
=
'
W
'
,
pady
=
40
)
self
.
button_two
.
grid
(
row
=
2
,
column
=
3
,
sticky
=
'
W
'
,
pady
=
0
)
self
.
auto_resize
(
row_count
=
2
,
column_count
=
3
,
heavy_column
=
3
)
#self.button_two.deselect()
self
.
update
()
self
.
mainloop
()
def
progress_bar
(
self
,
message
=
None
,
icon_path
=
None
,
icon_name
=
None
,
size
=
0
,
interval
=
100
,
pb_queue
=
None
):
#Best effort attempt at a real progress bar
# This is what Tk calls "determinate mode" rather than "indeterminate mode"
#size: denominator of percent complete
#interval: frequency, in ms, of how often to poll the file for progress
#pb_queue: queue object used to send updates to the bar
self
.
find_icon
(
icon_path
,
icon_name
)
self
.
text_label
=
tk
.
Label
(
text
=
message
)
self
.
set_colors
(
self
.
text_label
)
self
.
set_colors
(
self
.
image_label
)
self
.
image_label
.
grid
(
row
=
1
,
column
=
1
,
sticky
=
'
NSEW
'
)
self
.
text_label
.
grid
(
row
=
2
,
column
=
1
,
sticky
=
'
NSEW
'
)
self
.
progress
=
ttk
.
Progressbar
(
self
,
style
=
'
black.Horizontal.TProgressbar
'
,
orient
=
"
horizontal
"
,
length
=
100
,
mode
=
"
determinate
"
)
self
.
progress
.
grid
(
row
=
3
,
column
=
1
,
sticky
=
'
NSEW
'
)
self
.
value
=
0
self
.
progress
[
"
maximum
"
]
=
size
self
.
auto_resize
(
1
,
3
)
self
.
queue
=
pb_queue
self
.
check_scheduler
()
def
check_scheduler
(
self
):
if
self
.
value
<
self
.
progress
[
"
maximum
"
]:
self
.
check_queue
()
self
.
after
(
100
,
self
.
check_scheduler
)
def
check_queue
(
self
):
while
self
.
queue
.
qsize
():
try
:
msg
=
float
(
self
.
queue
.
get
(
0
))
#custom signal, time to tear down
if
msg
==
-
1
:
self
.
choice
.
set
(
True
)
self
.
destroy
()
else
:
self
.
progress
.
step
(
msg
)
self
.
value
=
msg
except
Queue
.
Empty
:
pass
class
ThreadedClient
(
threading
.
Thread
):
#for test only, not part of the functional code
def
__init__
(
self
,
queue
):
threading
.
Thread
.
__init__
(
self
)
self
.
queue
=
queue
def
run
(
self
):
for
x
in
range
(
1
,
90
,
10
):
time
.
sleep
(
1
)
print
"
run
"
+
str
(
x
)
self
.
queue
.
put
(
10
)
#tkk progress bars wrap at exactly 100 percent, look full at 99%
print
"
leftovers
"
self
.
queue
.
put
(
9
)
time
.
sleep
(
5
)
# -1 is a custom signal to the progress_bar to quit
self
.
queue
.
put
(
-
1
)
if
__name__
==
"
__main__
"
:
#When run as a script, just test the InstallUserMessage.
#To proceed with the test, close the first window, select on the second. The third will close by itself.
from
contextlib
import
closing
from
multiprocessing
import
Process
import
sys
import
tempfile
def
set_and_check
(
frame
,
value
):
print
"
value:
"
+
str
(
value
)
frame
.
progress
.
step
(
value
)
if
frame
.
progress
[
"
value
"
]
<
frame
.
progress
[
"
maximum
"
]:
print
"
In Progress
"
else
:
print
"
Over now
"
#basic message window test
frame2
=
InstallerUserMessage
(
text
=
"
Something in the way she moves....
"
,
title
=
"
Beatles Quotes for 100
"
)
frame2
.
basic_message
(
message
=
"
...attracts me like no other.
"
,
icon_name
=
"
head-sl-logo.gif
"
)
print
"
Destroyed!
"
sys
.
stdout
.
flush
()
#binary choice test. User destroys window when they select.
frame3
=
InstallerUserMessage
(
text
=
"
Something in the way she knows....
"
,
title
=
"
Beatles Quotes for 200
"
)
frame3
.
binary_choice_message
(
message
=
"
And all I have to do is think of her.
"
,
icon_name
=
"
head-sl-logo.gif
"
,
one
=
"
Don
'
t want to leave her now
"
,
two
=
'
You know I believe and how
'
)
print
frame3
.
choice
.
get
()
sys
.
stdout
.
flush
()
#progress bar
queue
=
Queue
.
Queue
()
thread
=
ThreadedClient
(
queue
)
thread
.
start
()
print
"
thread started
"
frame4
=
InstallerUserMessage
(
text
=
"
Something in the way she knows....
"
,
title
=
"
Beatles Quotes for 300
"
)
frame4
.
progress_bar
(
message
=
"
You
'
re asking me will my love grow
"
,
icon_name
=
"
head-sl-logo.gif
"
,
size
=
100
,
pb_queue
=
queue
)
print
"
frame defined
"
frame4
.
mainloop
()
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment