#!/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=1, row=1, sticky=(tk.W, tk.E)) 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=2, row=1) self.startButton = ttk.Button(mainframe, text="Sync", command=master.start) self.startButton.grid(column=2,row=2) for child in mainframe.winfo_children(): child.grid_configure(padx=5, pady=5) self.master.focus_force() class Refresh: def __init__(self): pass def apply(self, gui): pass class DescriptionRefresh(Refresh): def __init__(self, description): GuiPart.Refresh.__init__(self) self.description = description def apply(self, gui): gui.master.title(self.description) gui.progress["value"] = 0 class UpdateRefresh(Refresh): def __init__(self, value): GuiPart.Refresh.__init__(self) self.value = value def apply(self, gui): gui.progress["value"] = self.value gui.percent.set("%.1f %%" % self.value) 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.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=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 def outputStat(self, stat): if 'totalFileCount' in stat: self.totalFileCount = int(stat['totalFileCount']) if 'totalFileSize' in stat: self.totalFileSize = int(stat['totalFileSize']) return P4.OutputHandler.HANDLED def outputMessage(self, message): print("Error: ", message) return P4.OutputHandler.CANCEL def init(self, type): self.type = type def setDescription(self, description, unit): print("Description: " , description) refresh = GuiPart.DescriptionRefresh(description) self.queue.put(refresh) print("TotalFileCount: ", self.totalFileCount) print("TotalFileSize: ", self.totalFileSize) def setTotal(self, total): 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.periodicCall() def periodicCall(self): self.gui.processIncoming() if self.finished: import sys sys.exit(0) self.after(100, self.periodicCall) 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 callback = P4Sync.Callback(self.queue) result = self.p4.run_sync(args, progress=callback, handler=callback, exception_level=0) self.finished = True if __name__ == '__main__': p4sync = P4Sync() p4sync.mainloop()