bypy/gui.py
#!/usr/bin/env python
# coding=utf-8
# A simple GUI for bypy, using Tkinter
# from __future__ imports must occur at the beginning of the file
from __future__ import unicode_literals
from __future__ import print_function
from __future__ import division
import sys
import threading
vi = sys.version_info
if vi[0] == 2:
import Tkinter as tk
import tkFileDialog
import tkMessageBox
import ttk
elif vi[0] == 3:
import tkinter as tk
from tkinter import filedialog as tkFileDialog, messagebox as tkMessageBox, ttk
from . import const
from . import monkey
from .util import (get_pcs_path)
from .tkutil import (
ColorMap, fgtag, bgtag,
Stretch, GridStyle, MyLogText,
centerwindow)
from .bypy import ByPy
GuiTitle = "Bypy GUI"
class RemoteListGui(tk.Toplevel):
# remotepath is the partial path,
# NOT including the '/apps/bypy' in front
def __init__(self, master, byp, remotepath = ''):
tk.Toplevel.__init__(self, master)
self.byp = byp
self.rpath = get_pcs_path(remotepath)
self.result = ''
self.transient(master)
self.master = master
title = 'Baidu: ' + self.rpath
self.title(title)
self.CreateWidgets()
self.geometry('400x300+0+0')
self.GetRemote()
self.grab_set()
def GetRemoteAct(self, r, args):
self.wList.delete(0, tk.END)
if self.rpath.strip('/') != const.AppPcsPath.strip('/'):
self.wList.insert(tk.END, '..')
try:
j = r.json()
for f in j['list']:
fullpath = f['path']
relpath = fullpath.split('/')[-1]
self.wList.insert(tk.END,
relpath + '/' if f['isdir'] else relpath)
return const.ENoError
except:
return const.EException
def GetRemote(self):
pars = {
'method' : 'list',
'path' : self.rpath,
'by' : 'name',
'order' : 'asc' }
result = self.byp._get(
const.PcsUrl + 'file', pars, self.GetRemoteAct)
if result == const.ENoError:
self.title(self.rpath)
else:
if self.rpath.strip('/') == const.AppPcsPath.strip('/'):
err = "Can't retrieve Baidu PCS directories!\n" + \
"Maybe the network is down?\n" + \
"You can still manually input the remote path though" + \
"(But, I doubt it will work)"
tkMessageBox.showerror(GuiTitle, err)
self.Bye()
else:
self.rpath = const.get_pcs_path('')
self.GetRemote()
def Bye(self, result = ''):
self.result = result
self.master.focus_set()
self.destroy()
def Delete(self, selected):
if tkMessageBox.askyesno(
title = GuiTitle,
message = "Are you sure you want to delete '{}' at Baidu?".format(selected),
parent = self):
rpath = '/'.join([self.rpath, selected])
r = self.byp._delete(rpath)
if r == const.ENoError:
self.wList.delete(tk.ANCHOR)
else:
tkMessageBox.showerror(
title = GuiTitle,
message = "Fail to delete '{}' at Baidu".format(selected),
parent = self)
def Select(self, event):
if event.widget == self.wOK:
self.Bye(self.rpath[len(const.AppPcsPath):])
elif event.widget == self.wList:
selected = ''
iet = int(event.type) # i don't know why, but it seems needed
if iet == 4: # mouse event. mouse is special or not?
selected = self.wList.get(self.wList.nearest(event.y))
elif iet == 2: # KeyPress
selected = self.wList.get(tk.ACTIVE)
if iet == 4 or \
(iet == 2 and event.keysym == 'Return'):
if selected[-1] == '/':
self.rpath = '/'.join([self.rpath, selected[:-1]])
self.GetRemote()
elif selected == '..':
self.rpath = '/'.join(self.rpath.split('/')[:-1])
self.GetRemote()
else:
self.Bye('/'.join([self.rpath, selected])[len(const.AppPcsPath):])
elif iet == 2 and event.keysym == 'Delete':
# don't handlle this, not so rational usage
if selected != '..':
self.Delete(selected)
def CreateWidgets(self):
self.grid_columnconfigure(0, weight = 1)
self.grid_rowconfigure(0, weight = 1)
self.wList = tk.Listbox(self)
self.wList.grid(sticky = Stretch, **GridStyle)
self.wList.bind('<Double-Button-1>', self.Select)
self.wList.bind('<Return>', self.Select)
self.wList.bind('<Delete>', self.Select)
self.wOK = tk.Button(self, text = 'OK', default = tk.ACTIVE)
self.wOK.grid(row = 1, column = 0, sticky = tk.E + tk.W, **GridStyle)
self.wOK.bind('<Button-1>', self.Select)
self.wOK.bind('<Return>', self.Select)
self.wList.focus_set()
self.protocol("WM_DELETE_WINDOW", lambda: ())
class BypyGui(tk.Frame):
# function remapping / hijacking
# pr and prcolor functions in console and GUI are different:
# in console: pr is the foundamental function, prcolor calls it
# in GUI: prcolor is the foundamental fucntion, pr calls it
def __init__(self, master = None):
tk.Frame.__init__(self, master)
self.master.title(GuiTitle)
self.grid(sticky = Stretch)
self.byp = None
self.threadrunning = False
self.localPath = tk.StringVar()
self.localPath.set('/tmp')
self.remotePath = tk.StringVar()
self.bLog = tk.IntVar()
self.bLog.set(1)
self.bSyncDelete = tk.IntVar()
self.bSyncDelete.set(0)
self.progress = tk.IntVar()
self.progress.set(0)
self.maxProgress = 1000
self.CreateWidgets()
monkey.setgui(self)
self.initbypy()
def CreateWidgets(self):
self.master.grid_columnconfigure(0, weight = 1)
self.master.grid_rowconfigure(1, weight = 1)
self.grid_columnconfigure(1, weight = 1)
self.grid_rowconfigure(4, weight = 1)
z = tk.Label(self, text = 'Baidu: ' + const.AppPcsPath)
z.grid(row = 0, column = 0, **GridStyle)
self.wRemotePath = tk.Entry(self, textvariable = self.remotePath)
self.wRemotePath.grid(row = 0, column = 1, sticky = Stretch, **GridStyle)
self.wRemoteSelect = tk.Button(self, text = 'R', underline = 0)
self.wRemoteSelect.grid(row = 0, column = 2, **GridStyle)
self.wRemoteSelect.bind('<Button-1>', self.selectremotepath)
self.bind_all('<Alt-r>', self.selectremotepath)
z = tk.Label(self, text = 'Local 本地')
z.grid(**GridStyle)
self.wLocalPath = tk.Entry(self, textvariable = self.localPath)
self.wLocalPath.grid(row = 1, column = 1, sticky = Stretch, **GridStyle)
self.wLocalSelect = tk.Button(self, text = 'L', underline = 0)
self.wLocalSelect.grid(row = 1, column = 2, **GridStyle)
self.wLocalSelect.bind('<Button-1>', self.selectlocalpath)
self.bind_all('<Alt-l>', self.selectlocalpath)
self.OpFrame = tk.Frame(self)
self.OpFrame.grid(row = 2, columnspan = 3, sticky = Stretch, **GridStyle)
self.OpFrame.grid_columnconfigure(0, weight = 1)
self.OpFrame.grid_columnconfigure(1, weight = 1)
self.OpFrame.grid_columnconfigure(2, weight = 1)
self.OpFrame.grid_columnconfigure(3, weight = 1)
self.OpFrame.grid_columnconfigure(3, weight = 1)
self.OpFrame.grid_columnconfigure(4, weight = 1)
self.wSyncUp = tk.Button(self.OpFrame, text = 'Sync Up 上传同步', underline = 5)
self.wSyncUp.grid(sticky = Stretch, **GridStyle)
self.wSyncUp.bind('<Button-1>', self.startsyncup)
self.bind_all('<Alt-u>', self.startsyncup)
self.wUpload = tk.Button(self.OpFrame, text = 'Upload 上传')
self.wUpload.grid(row = 0, column = 1, sticky = Stretch, **GridStyle)
self.wUpload.bind('<Button-1>', self.startupload)
self.wSyncDown = tk.Button(self.OpFrame, text = 'Sync Down 下载同步', underline = 5)
self.wSyncDown.grid(row = 0, column = 2, sticky = Stretch, **GridStyle)
self.wSyncDown.bind('<Button-1>', self.startsyncdown)
self.bind_all('<Alt-d>', self.startsyncdown)
self.wDownload = tk.Button(self.OpFrame, text = 'Download 下载')
self.wDownload.grid(row = 0, column = 3, sticky = Stretch, **GridStyle)
self.wDownload.bind('<Button-1>', self.startdownload)
self.wSyncDelete = tk.Checkbutton(self.OpFrame, text = 'Sync Del 同步删除', underline = 7, variable = self.bSyncDelete)
self.wSyncDelete.configure(foreground = 'red')
self.wSyncDelete.grid(row = 0, column = 4, sticky = Stretch, **GridStyle)
self.bind_all('<Alt-l>', lambda e: self.bSyncDelete.set(1 if self.bSyncDelete.get() == 0 else 0))
self.wEnableLog = tk.Checkbutton(self.OpFrame, text = 'Log 日志', underline = 2, variable = self.bLog)
self.wEnableLog.grid(row = 0, column = 5, sticky = Stretch, **GridStyle)
self.bind_all('<Alt-g>', lambda e: self.bLog.set(1 if self.bLog.get() == 0 else 0))
self.wCompare = tk.Button(self.OpFrame, text = 'Compare Dir 比较目录')
self.wCompare.grid(row = 1, column = 0, sticky = Stretch, **GridStyle)
self.wCompare.bind('<Button-1>', self.startcompare);
self.wProgressBar = ttk.Progressbar(
self, maximum = self.maxProgress, variable = self.progress,
mode = 'determinate')
self.wProgressBar.grid(row = 3, column = 0, columnspan = 3, sticky = Stretch, **GridStyle)
self.wLog = MyLogText(self)
self.wLog.grid(row = 4, column = 0, columnspan = 3, sticky = Stretch, **GridStyle)
self.wClearLog = tk.Button(self.OpFrame, text = 'Clear Log 清空日志')
self.wClearLog.grid(row = 1, column = 5, sticky = Stretch, **GridStyle)
self.wClearLog.bind('<Button-1>',
lambda e: self.wLog.delete('1.0', tk.END))
#self.wLog.tag_add(fgtag(''))
#self.wLog.tag_add(bgtag(''))
for k, v in ColorMap.items():
ft = fgtag(v)
bt = bgtag(v)
#self.wLog.tag_add(ft)
self.wLog.tag_config(ft, foreground = v)
#self.wLog.tag_add(bt)
self.wLog.tag_config(bt, background = v)
centerwindow(self.master)
def initbypy(self):
self.byp = ByPy(verbose = 1)
def selectlocalpath(self, *args):
self.localPath.set(
tkFileDialog.askopenfilename(
title = "Please select a local file " + \
"(remove the file name later if you " + \
"want to select a directory)",
initialdir = self.wLocalPath,
parent = self))
def selectremotepath(self, *args):
remoteList = RemoteListGui(self, self.byp, self.remotePath.get())
centerwindow(remoteList)
remoteList.wait_window(remoteList)
self.remotePath.set(remoteList.result)
def syncupproc(self, lpath, rpath, delete):
self.byp.syncup(lpath, rpath, delete)
self.threadrunning = False
def startsyncup(self, *args):
if not self.threadrunning:
self.threadrunning == True
threading.Thread(target = self.syncupproc,
args = (
self.localPath.get(),
self.remotePath.get(),
self.bSyncDelete.get())).start()
def uploadproc(self, lpath, rpath):
self.byp.upload(lpath, rpath)
self.threadrunning = False
def startupload(self, *args):
if not self.threadrunning:
self.threadrunning == True
threading.Thread(target = self.uploadproc,
args = (
self.localPath.get(),
self.remotePath.get())).start()
def syncdownproc(self, rpath, lpath, delete):
self.byp.syncdown(rpath, lpath, delete)
self.threadrunning = False
def startsyncdown(self, *args):
if not self.threadrunning:
self.threadrunning == True
threading.Thread(target = self.syncdownproc,
args = (
self.remotePath.get(),
self.localPath.get(),
self.bSyncDelete.get())).start()
def downloadproc(self, rpath, lpath):
if len(rpath) == 0 or rpath[-1] == '/':
self.byp.downdir(rpath, lpath)
else:
self.byp.downfile(rpath, lpath)
self.threadrunning = False
def startdownload(self, *args):
if not self.threadrunning:
self.threadrunning == True
threading.Thread(target = self.downloadproc,
args = (
self.remotePath.get(),
self.localPath.get())).start()
def compareproc(self, rpath, lpath):
self.byp.compare(rpath, lpath)
self.threadrunning = False
def startcompare(self, *args):
if not self.threadrunning:
self.threadrunning == True
threading.Thread(target = self.compareproc,
args = (
self.remotePath.get(),
self.localPath.get())).start()
def main():
tkRoot = tk.Tk()
ui = BypyGui(tkRoot)
ui.mainloop()
if __name__ == '__main__':
main()
def unused():
''' just prevent unused warnings '''
tkMessageBox.showinfo('')
# vim: tabstop=4 noexpandtab shiftwidth=4 softtabstop=4 ff=unix fileencoding=utf-8