Pobkup/pobkup.py


Home Back

#!/usr/bin/env python3
import wx, threading, gettext, configparser, sys, subprocess, shlex,os, locale
from gui_utils import *
from wx import xrc

import utils, gui_utils
from profiles import *

VERSION="0.1"


class MainApp(wx.App):
    
    currProfile=None
    imWorking=False

    def OnInit(self):
        self.res = xrc.XmlResource(os.path.join('gui','mainFrame.xrc'))
        self.init_frame()
        return True

    def init_frame(self):
        self.proc=None
        self.frame = self.res.LoadFrame(None, 'mainFrame')
        self.frame.SetIcon(wx.Icon("icon.ico"))
        self.btBackup = xrc.XRCCTRL(self.frame, 'btBackup')
        self.lstOutput= xrc.XRCCTRL(self.frame, 'lstOutput')
        self.cmbProfiles=xrc.XRCCTRL(self.frame, 'cmbProfiles')
        self.btEditProfile=xrc.XRCCTRL(self.frame, 'btEditProfile')
        self.lblStatus=xrc.XRCCTRL(self.frame, 'lblStatus')
        self.chkPoweroff=xrc.XRCCTRL(self.frame, 'chkPoweroff')
        
        #Getting menu
        self.menubar = self.frame.GetMenuBar()
        self.mnuItemExit = self.menubar.FindItemById(xrc.XRCID('mnuItemExit'))
        self.mnuItemNewProfile = self.menubar.FindItemById(xrc.XRCID('mnuItemNewProfile')) 
        self.mnuItemAbout = self.menubar.FindItemById(xrc.XRCID('mnuItemAbout'))
        self.mnuItemEnableScheduler = self.menubar.FindItemById(xrc.XRCID('mnuItemEnableScheduler'))
        self.mnuItemDisableScheduler = self.menubar.FindItemById(xrc.XRCID('mnuItemDisableScheduler'))
            
        
        #Bind event Menu
        self.frame.Bind(wx.EVT_MENU, self.quit, self.mnuItemExit)
        self.frame.Bind(wx.EVT_MENU, self.newProfiles, self.mnuItemNewProfile)
        self.frame.Bind(wx.EVT_MENU, self.onAbout, self.mnuItemAbout)
        
        self.frame.Bind(wx.EVT_MENU, self.enableScheduler, self.mnuItemEnableScheduler)
        self.frame.Bind(wx.EVT_MENU, self.disableScheduler, self.mnuItemDisableScheduler)
        
        #Init ListCtrl
        self.lstOutput.InsertColumn(0, "File")
        self.lstOutput.SetColumnWidth(0, -2)
        self.progress=xrc.XRCCTRL(self.frame, 'progress')
        
        
        #Bind events widgets
        self.btBackup.Bind(wx.EVT_BUTTON, self.onStartStop)
        self.cmbProfiles.Bind(wx.EVT_COMBOBOX, self.setProfile)
        self.frame.Bind(wx.EVT_CLOSE, self.quit)
        self.btEditProfile.Bind(wx.EVT_BUTTON, self.editProfile)
        
        #Labels 
        self.lblStatus.SetLabel(_("Ready."))
        self.chkPoweroff.SetLabel(_("Poweroff at the end"))
        self.mnuItemNewProfile.SetItemLabel(_("New Profile\tCTRL+n"))
        self.mnuItemExit.SetItemLabel(_("Exit\tCTRL+q"))
        
        self.loadCmbProfiles()
        self.frame.Show()
    
    def enableScheduler(self, evt):
        if sys.platform=="darwin":
            Info(self.frame, _("Please enable manually ' python3 pobkupd.py' at the boot"), caption = 'Info')
            return
        
        #On mate desktop you have to do manually
        if (utils.checkIsRunning("mate-session")):
            Info(self.frame, _("Please enable manually ' python3 pobkupd.py' at the boot"), caption = 'Info')
            subprocess.Popen(['mate-session-properties'])
            return
        
        
        if (utils.installPobkupd()==True):
            Info(self.frame, _("Pobkupd is enabled. Please restart session to run it"), caption = 'Info')
        else:
            Warn(self.frame, _("Error on enable Pobkupd !"), _("Warning !"))
        
        
    
    def disableScheduler(self, evt):
        if (utils.removePobkupd()==True):
            Info(self.frame, _("Pobkupd is disabled. Please restart session to stop it"), caption = 'Info')
        else:
            Warn(self.frame, _("Error on disable Pobkupd !"), _("Warning !"))
        
    def setStatus(self,s):
        wx.CallAfter(self.lblStatus.SetLabel, s)
    
    def quit(self,evt):
        r= YesNo(self.frame, _("Do you want exit?"), caption = 'Exit')
        if (r==True):
            self.Destroy()
            sys.exit()
            
            
    def loadCmbProfiles(self):
        self.config = configparser.ConfigParser()
        self.config.read(utils.getProfilesFilePath())
        profiles=self.config.sections()
        self.cmbProfiles.Clear()
        for p in profiles:
            self.cmbProfiles.Append(p)
        
        self.cmbProfiles.SetSelection(self.cmbProfiles.GetCount()-1)    
        self.currProfile=self.cmbProfiles.GetValue()
        
    def newProfiles(self,evt):
        pr= Profiles()
        pr.OnInit()
        pr.dialog.ShowModal()
        self.loadCmbProfiles()
    
    def editProfile(self,evt):
        pr= Profiles()
        pr.OnInit()
        pr.setProfile(self.currProfile)
        pr.dialog.ShowModal()
        self.loadCmbProfiles()
        
        
    def setProfile(self, evt):
        self.currProfile=self.cmbProfiles.GetValue()
    
    def initComboBoxProfiles(self, evt):
        self.config = configparser.ConfigParser()
        self.config.read(utils.getProfilesFilePath())
        profiles=self.config.sections()
        self.cmbProfiles.Clear()
        for p in profiles:
            self.cmbProfiles.Append(p)
        
        self.cmbProfiles.SetSelection(self.cmbProfiles.GetCount()-1)    
        self.currProfile=self.cmbProfiles.GetValue()
    
    def busyGui(self):
        wx.CallAfter(self.btBackup.SetLabel, "Stop!")
        wx.CallAfter(self.cmbProfiles.Enable, False)
        wx.CallAfter(self.btEditProfile.Enable, False)
    
    def readyGui(self):
        wx.CallAfter(self.btBackup.SetLabel, "Backup!")
        wx.CallAfter(self.cmbProfiles.Enable, True)
        wx.CallAfter(self.btEditProfile.Enable, True)
    
        
    
    def    appendOutput(self,s):
        count=self.lstOutput.GetItemCount()
        wx.CallAfter(self.lstOutput.InsertItem,count,s)
        wx.CallAfter(self.lstOutput.Focus, count)
    
    def cleanOutput(self):
        wx.CallAfter(self.lstOutput.DeleteAllItems)    
    
    def getPercent(self,line):
        # Example: 11,131,595   1%   10.47MB/s    0:00:00 (xfr#16, to-chk=2761/4538)
        line=line.split(" ")
        for l in line:
            if ('%' in l):
                if (len(l)==2): return l[0]
                if (len(l)==3): return l[0]+l[1]
                if (len(l)==4): return l[0]+l[1]+l[2]        
                
    def startStopBackup(self):
        self.imWorking=True
        self.busyGui()
        rsyncBin=utils.getRsyncPath()
        self.config.read(utils.getProfilesFilePath())
        if (not sys.platform == 'win32'): 
            src=self.config[self.currProfile]['src']
            dst=self.config[self.currProfile]['dst']
        else:
            src=utils.PathWinToUnix(self.config[self.currProfile]['src'])
            dst=utils.PathWinToUnix(self.config[self.currProfile]['dst'])    
        
        delete=self.config.getboolean(self.currProfile,'delete')
        history=self.config.getboolean(self.currProfile,'history')
        excludeFile=self.config[self.currProfile]['excludeFile']
        logfile=self.config[self.currProfile]['logfile']
        
        
        cmd=[rsyncBin, '-avh', '--info=progress2']
        if (delete==True): cmd.append('--delete')
        if (len(excludeFile)>1) : cmd.append("--exclude-from="+excludeFile)
        if (history==True): dst+=utils.getTimeHistoryFolder()
        if (logfile!=None): cmd.append("--log-file="+logfile) 
        cmd.append(src)
        cmd.append(dst)
        
            
        
        
        fileOutErr = open(utils.getTempDir()+"pobkup-errors.txt","w")
        print(cmd)
        if (not sys.platform == 'win32'): 
            self.proc = subprocess.Popen(cmd, bufsize=0 ,stdout=subprocess.PIPE, stderr=fileOutErr, shell=False)
        else:
            si = subprocess.STARTUPINFO()
            si.dwFlags |= subprocess.STARTF_USESHOWWINDOW
            self.proc = subprocess.Popen(cmd, bufsize=0 ,stdout=subprocess.PIPE, stderr=fileOutErr, startupinfo=si)
            
        while self.proc.poll() is None :
            out = self.proc.stdout.readline()
            
            #Print Output
            self.appendOutput(str(out.decode("utf-8")))
            #wx.CallAfter(self.appendOutput, str(out.decode("utf-8")))
            
            #Print Progress
            if (self.getPercent(str(out.decode("utf-8"))) is not None):
                p=float(self.getPercent(str(out.decode("utf-8"))))
                wx.CallAfter(self.progress.SetValue,p)
                self.setStatus(_("Progress: ")+str(int(p))+'%')
                
        #Print error at the end
        if (os.stat(utils.getTempDir()+"pobkup-errors.txt").st_size != 0):
            fileOutErr= open(utils.getTempDir()+"pobkup-errors.txt","r")
            listErr=fileOutErr.readlines()
            for e in listErr:
                self.appendOutput(e)    
        
        #End of jobs
        fileOutErr.close()
        self.readyGui()
        self.imWorking=False
        self.appendOutput(_("BACKUP TERMINATED."))
        wx.CallAfter(self.progress.SetValue,100)
        self.setStatus(_("BACKUP TERMINATED."))
        if(self.chkPoweroff.IsChecked()):
            print("Poweroff")
            utils.poweroff()
        utils.notifySend("Pobkup", _("BACKUP TERMINATED."))
        
        #ExecuteAfter at the end of job
        cmdAfter=self.config[self.currProfile]['cmdafter']
        if(cmdAfter!=None): os.system(cmdAfter)
    
    def onStartStop(self, evt):
        
        #ExecuteBefore
        try:
            cmdBefore=self.config[self.currProfile]['cmdbefore']
            if(cmdBefore!=None): os.system(cmdBefore)
        except:
            pass
        
        
        #Check src and dest
        src=self.config[self.currProfile]['src']
        dst=self.config[self.currProfile]['dst']
        if (not utils.checkPath(src)):
            wx.CallAfter(Warn, self.frame, _("Source not present"), _("Warning"))
            self.readyGui()
            return
        if (not utils.checkPath(dst)):
            wx.CallAfter(Warn, self.frame, _("Destination not present"), _("Warning"))
            self.readyGui()
            return
        
        self.cleanOutput()
        self.progress.SetValue(0)
        
        
        if (self.imWorking==False):
            self.thdBackup = threading.Thread(target=self.startStopBackup)
            self.thdBackup.daemon = True
            self.thdBackup.start()
        else :
            self.proc.kill()
            self.readyGui()
            self.imWorking==False
            self.appendOutput(_("BACKUP TERMINATED BY USER."))
        
    
    def onAbout(self, evt):
        import wx.adv
        aboutInfo = wx.adv.AboutDialogInfo()
        aboutInfo.SetName("Pobkup")
        aboutInfo.SetVersion(VERSION)
        aboutInfo.SetIcon(wx.Icon('icons/icon.png', wx.BITMAP_TYPE_PNG))
        aboutInfo.SetDescription(_("A simple gui for Rsync"))
        aboutInfo.SetCopyright("Released under GNU/GPL v3 License \n\n Author: Fabio Di Matteo - fadimatteo@gmail.com")
        aboutInfo.SetWebSite("https://github.com/pobfdm/pobkup")
        aboutInfo.AddDeveloper("Fabio Di Matteo - fadimatteo@gmail.com")
        aboutInfo.AddArtist("Arthur Zaynullin https://www.iconfinder.com/Ampeross")
        wx.adv.AboutBox(aboutInfo)

        
            
if __name__ == '__main__':
    
    #Gettext
    try:
        current_locale, encoding = locale.getdefaultlocale()
        locale_path = 'locale'
        language = gettext.translation ('pobkup', locale_path, [current_locale] )
        language.install()
    except:
        print("Locale not found")
        _ = gettext.gettext 
    
    utils.initConfig()
    app = MainApp(False)
    app.MainLoop()

Powered by Code, a simple repository browser by Fabio Di Matteo