#!/usr/bin/env python3 # # Copyright (C) 2012 Sven Erik Knop, Perforce Software Ltd # # Idea: # Sync files from the default location (or, with -p, -u, -c, -H, -P options) # Show progress using the Tkinter.tkk ProgressBar, P4.OutputHandler and P4.Progress # from __future__ import print_function from argparse import ArgumentParser import P4 import Tkinter as tk import ttk import threading import Queue class P4Command: def __init__(self): self.parser = ArgumentParser( description=self.description(), epilog="Copyright (C) 2012 Sven Erik Knop, Perforce Software Ltd" ) self.parser.add_argument('-p', '--port', help="P4PORT") self.parser.add_argument('-c', '--client', help="P4CLIENT") self.parser.add_argument('-u', '--user', help="P4USER") self.parser.add_argument('-H', '--host', help="P4HOST") self.parser.add_argument('-P', '--password', help="P4PASSWD") self.parser.add_argument('-d', '--directory', help="CWD") self.addArguments() self.myOptions = self.parser.parse_args() self.p4 = P4.P4() if self.myOptions.port: self.p4.port = self.myOptions.port if self.myOptions.client: self.p4.client = self.myOptions.client if self.myOptions.user: self.p4.user = self.myOptions.user if self.myOptions.host: self.p4.host = self.myOptions.host if self.myOptions.password: self.p4.password = self.myOptions.password if self.myOptions.directory: self.p4.cwd = self.myOptions.directory def description(self): return "P4Command" def addArguments(self): pass class GuiPart: def __init__(self, master, mainframe, queue): self.queue = queue self.master = master self.progress = ttk.Progressbar(mainframe, orient="horizontal", length=300, mode="determinate") self.progress.grid(column=0, row=0, sticky="EW") self.progress["value"] = 0 self.progress["maximum"] = 100 self.percent = tk.StringVar() self.percent.set("0.0 %") ttk.Label(mainframe, textvariable=self.percent).grid(column=1, row=0) frame = ttk.Frame(mainframe) frame.grid(column=0, row=1,sticky="NSEW") frame.columnconfigure(0, weight=1) frame.rowconfigure(0, weight=1) self.yscrollbar= ttk.Scrollbar(frame, orient=tk.VERTICAL) self.yscrollbar.grid(column=1, row=0, sticky="NS") self.xscrollbar = ttk.Scrollbar(frame, orient=tk.HORIZONTAL) self.xscrollbar.grid(column=0, row=1, sticky="EW") self.listbox = tk.Listbox(frame, height=10,width=40, yscrollcommand=self.yscrollbar.set, xscrollcommand=self.xscrollbar.set) self.listbox.grid(column=0, row=0, sticky="NSEW" ) self.yscrollbar["command"] = self.listbox.yview self.xscrollbar["command"] = self.listbox.xview self.button = ttk.Button(mainframe, text="Sync", command=master.start) self.button.grid(column=1,row=1,sticky=tk.S) for child in mainframe.winfo_children(): child.grid_configure(padx=5, pady=5) class StartRefresh: def __init__(self): pass def apply(self, gui): gui.button.configure(text="Cancel") gui.button.configure(command=gui.master.cancel) class FinishedRefresh: def __init__(self): pass def apply(self, gui): gui.button.configure(text="Quit") gui.button.configure(command=gui.master.quit) class DescriptionRefresh: def __init__(self, description): self.description = description def apply(self, gui): gui.master.title(self.description) gui.progress["value"] = 0 class UpdateRefresh: def __init__(self, value): self.value = value def apply(self, gui): gui.progress["value"] = self.value gui.percent.set("%.1f %%" % self.value) class DepotFileRefresh: def __init__(self, filename): self.filename = filename def apply(self, gui): gui.listbox.insert(tk.END, self.filename + "\n") gui.listbox.yview(tk.END) def processIncoming(self): """Read from queue, process results""" while self.queue.qsize(): try: refresh = self.queue.get(0) refresh.apply(self) except Queue.Empty: pass class P4Sync(P4Command, tk.Tk, P4.Progress, P4.OutputHandler): def __init__(self): P4Command.__init__(self) tk.Tk.__init__(self) P4.Progress.__init__(self) P4.OutputHandler.__init__(self) self.columnconfigure(0, weight=1) self.rowconfigure(0, weight=1) self.title("P4 Sync") self.mainframe = ttk.Frame(self, padding="3 3 12 12 ") self.mainframe.grid(column=0, row=0, sticky=(tk.N, tk.W, tk.E, tk.S)) self.mainframe.columnconfigure(0, weight=1) self.mainframe.rowconfigure(0, weight=0) # don't move the scrollbar up or down self.mainframe.columnconfigure(1, weight=0) self.mainframe.rowconfigure(1, weight=1) self.queue = Queue.Queue() self.gui = GuiPart(self,self.mainframe, self.queue) self.finished = False def addArguments(self): self.parser.add_argument('-n', '--preview', dest='syncOptions', action='append_const', const='-n', help="Preview only, no syncing") self.parser.add_argument('--force', dest='syncOptions', action='append_const', const='-f', help="Force the update regardless of previous syncs") self.parser.add_argument('--print', dest='syncOptions', action='append_const', const='-p', help="Update local workspace without updating the database") self.parser.add_argument('--keep', dest='syncOptions', action='append_const', const='-k', help="Update database without updating the local workspace") self.parser.add_argument('filepaths', type=str, nargs='*', help="[file[revRange] ...]") def description(self): return "P4Sync" class Callback(P4.Progress, P4.OutputHandler): def __init__(self, queue): self.queue = queue self.totalFileCount = 0 self.totalFileSize = 0 self.response=P4.OutputHandler.HANDLED def outputStat(self, stat): if 'totalFileCount' in stat: self.totalFileCount = int(stat['totalFileCount']) if 'totalFileSize' in stat: self.totalFileSize = int(stat['totalFileSize']) if 'depotFile' in stat: refresh = GuiPart.DepotFileRefresh(stat['depotFile'] + "#" + stat['rev']) self.queue.put(refresh) return self.response def outputMessage(self, message): print("Error: ", message) return P4.OutputHandler.CANCEL def init(self, type): self.type = type def setDescription(self, description, unit): refresh = GuiPart.DescriptionRefresh(description) self.queue.put(refresh) # print("TotalFileCount: ", self.totalFileCount) # print("TotalFileSize: ", self.totalFileSize) def setTotal(self, total): pass # print("Total: %s" % total) def update(self, position): self.position = position # print("Position: %s" % position) refresh = GuiPart.UpdateRefresh(100 * int(position) / self.totalFileCount) self.queue.put( refresh ) def done(self, fail): self.fail = fail # print("Done") def start(self): self.thread = threading.Thread(target=self.runInThread) self.thread.start() self.queue.put(GuiPart.StartRefresh()) self.periodicCall() def cancel(self): self.callback.response=P4.OutputHandler.CANCEL def periodicCall(self): self.gui.processIncoming() if self.finished: self.queue.put(GuiPart.FinishedRefresh()) self.after(100, self.periodicCall) def quit(self): import sys sys.exit(0) def runInThread(self): with self.p4.connect(): args = ['-q'] if self.myOptions.syncOptions: # is None if no options specified args += self.myOptions.syncOptions args += self.myOptions.filepaths self.callback = P4Sync.Callback(self.queue) result = self.p4.run_sync(args, progress=self.callback, handler=self.callback, exception_level=0) self.finished = True if __name__ == '__main__': p4sync = P4Sync() p4sync.mainloop()