예제 #1
0
def main():
    Updater.check_update()
    app = MainApp()
    app.MainLoop()
예제 #2
0
class Gui():
    def __init__(self):

        ## Set version (tag) of release here!
        self.version = 1.0

        self.setupEnv()
        self.setupGui()

    def setupEnv(self):

        self.appDataDir = "{}/PCModManager".format(getenv("APPDATA"))
        self.dataDir = "{}/Data".format(self.appDataDir)
        self.modsJsonPath = "{}/mods.json".format(self.dataDir)

        if path.exists(self.appDataDir) == False:
            mkdir(self.appDataDir)
            print("Appdata doesn't exist, creating it...")

        if path.exists(self.dataDir) == False:
            mkdir(self.dataDir)

        if path.exists(self.modsJsonPath) == False:
            with open(self.modsJsonPath, "w"):
                pass

    def setupGui(self):

        self.mainWindow = tk.Tk()

        self.mainWindow.geometry("400x800+600+350")
        self.mainWindow.title("PC Mod Manager")

        self.modList = self.loadModsList()

        global dir_path

        dir_path = path.dirname(path.realpath(__file__)).replace("\\", "/")

        self.mainWindow.iconbitmap(dir_path + '/Data/icon.ico')

        self.styles()
        self.mainWindow.tk.call("lappend", "auto_path",
                                "{}/awthemes-9.3.1/".format(dir_path))

        self.mainWindow.tk.call("package", "require", 'awdark')
        self.mainWindow.tk.call("package", "require", 'awlight')

        self.style.theme_use('awdark')

        self.mainWindow.configure(bg=self.style.lookup('TFrame', 'background'))

        self.mainWindow.grid_columnconfigure(0, weight=1)
        self.mainWindow.grid_rowconfigure(0, weight=1)

        self.tabs = ttk.Notebook(self.mainWindow, takefocus=False)
        self.tabs.grid(row=0, column=0, sticky="NSEW")

        self.manageModsFrame = ttk.Frame(self.tabs)
        self.manageModsFrame.grid_columnconfigure(0, weight=1)
        self.manageModsFrame.grid_columnconfigure(1, weight=1)
        self.manageModsFrame.grid_rowconfigure(1, weight=1)

        self.packModFrame = ttk.Frame(self.tabs)
        self.packModFrame.grid_columnconfigure(0, weight=1)
        self.packModFrame.grid_rowconfigure(1, weight=1)

        self.tabs.add(self.manageModsFrame, text="Manage", sticky="nsew")
        self.tabs.add(self.packModFrame, text="Export", sticky="nsew")

        self.manageModsLabel = ttk.Label(self.manageModsFrame,
                                         text="Manage Mods",
                                         font=self.headerFont,
                                         anchor="center")
        self.manageModsLabel.grid(row=0, column=0, sticky="NEW", columnspan=2)

        self.manageModsScrollFrame = ScrollableFrame(self.manageModsFrame,
                                                     self)
        self.manageModsScrollFrame.grid(row=1,
                                        column=0,
                                        sticky="NESW",
                                        columnspan=2)

        self.modWidgetList = []
        for i, mod in enumerate(self.modList):
            widget = ModTileWidget(mod, self,
                                   self.manageModsScrollFrame.scrollable_frame,
                                   i, 0)
            self.modWidgetList.append(widget)

        self.manageModsButtonFrame = ttk.Frame(self.manageModsFrame)
        self.manageModsButtonFrame.grid(row=2,
                                        column=0,
                                        columnspan=2,
                                        sticky="nsew")

        self.manageModsButtonFrame.grid_columnconfigure(0,
                                                        weight=0,
                                                        uniform="button")
        self.manageModsButtonFrame.grid_columnconfigure(1,
                                                        weight=0,
                                                        uniform="button")
        self.manageModsButtonFrame.grid_columnconfigure(2,
                                                        weight=0,
                                                        uniform="button")

        self.manageModsInstallButton = ttk.Button(
            self.manageModsButtonFrame,
            text="Install new mod",
            command=lambda: self.inject_mod(
                askopenfilename(initialdir=dir_path,
                                filetypes=[("PC Mod Package", "*.pcm")])))
        self.manageModsInstallButton.grid(row=0, column=0, sticky="nsew")

        self.manageModsRestoreButton = ttk.Button(
            self.manageModsButtonFrame,
            text="Uninstall all mods",
            command=lambda: self.restore())
        self.manageModsRestoreButton.grid(row=0, column=1, sticky="nsew")

        self.manageModsLaunchGameButton = ttk.Button(
            self.manageModsButtonFrame,
            text="Launch Planet Coaster",
            command=lambda: subprocess.run("cmd /c start steam://run/493340"))
        self.manageModsLaunchGameButton.grid(row=0, column=2, sticky="nsew")

        self.exportModsLabel = ttk.Label(self.packModFrame,
                                         text="Pack PCM",
                                         font=self.headerFont,
                                         anchor="center")
        self.exportModsLabel.grid(row=0, column=0, sticky="NEW")

        self.exportModCanvasFrame = ScrollableFrame(self.packModFrame, self)
        self.exportModCanvasFrame.grid(row=1, column=0, sticky="nsew")

        self.exportModCanvasFrame.grid_columnconfigure(0, weight=1)

        self.exportModMetaFrame = ttk.Frame(self.packModFrame)
        self.exportModMetaFrame.grid(row=2, column=0, sticky="nsew")

        self.metaNameLabel = ttk.Label(self.exportModMetaFrame,
                                       text="Name:",
                                       font=font.Font(size=11, weight="bold"))
        self.metaNameEntry = ttk.Entry(self.exportModMetaFrame)

        self.metaDescLabel = ttk.Label(self.exportModMetaFrame,
                                       text="Description:",
                                       font=font.Font(size=11, weight="bold"))
        self.metaDescEntry = ttk.Entry(self.exportModMetaFrame)

        self.metaAuthLabel = ttk.Label(self.exportModMetaFrame,
                                       text="Author(s):",
                                       font=font.Font(size=11, weight="bold"))
        self.metaAuthEntry = ttk.Entry(self.exportModMetaFrame)

        self.metaNameEntry.insert(0, "Unnamed mod")
        self.metaDescEntry.insert(0, "A default mod description")
        self.metaAuthEntry.insert(0, "Unnamed")

        self.metaNameLabel.grid(row=0, column=0, sticky="nsew")
        self.metaNameEntry.grid(row=1, column=0, sticky="nsew")
        self.metaDescLabel.grid(row=2, column=0, sticky="nsew")
        self.metaDescEntry.grid(row=3, column=0, sticky="nsew")
        self.metaAuthLabel.grid(row=4, column=0, sticky="nsew")
        self.metaAuthEntry.grid(row=5, column=0, sticky="nsew")

        self.exportModMetaFrame.grid_columnconfigure(0, weight=1)

        self.exportModToolbarFrame = ttk.Frame(self.packModFrame)
        self.exportModToolbarFrame.grid(row=3, column=0, sticky="nsew")

        self.exportModCreateNew = ttk.Button(
            self.exportModToolbarFrame,
            text="Add File",
            command=lambda: self.createNewExportFile())
        self.exportModExport = ttk.Button(self.exportModToolbarFrame,
                                          text="Pack Mod",
                                          command=lambda: self.pack())
        self.RemoveAllPackFilesButton = ttk.Button(
            self.exportModToolbarFrame,
            text="Remove All",
            command=lambda: self.RemoveAllPackFiles())

        self.exportModCreateNew.grid(row=0, column=0, sticky="nsew")
        self.exportModExport.grid(row=0, column=1, sticky="nsew")
        self.RemoveAllPackFilesButton.grid(row=0, column=2, sticky="nsew")

        self.exportModToolbarFrame.grid_columnconfigure(0,
                                                        weight=0,
                                                        uniform="button")
        self.exportModToolbarFrame.grid_columnconfigure(2,
                                                        weight=0,
                                                        uniform="button")
        self.exportModToolbarFrame.grid_rowconfigure(0, weight=1)
        self.packModFrame.grid_rowconfigure(2, weight=0, minsize=45)

        self.tabs.bind("<<NotebookTabChanged>>",
                       lambda event: self.mainWindow.focus_set())

        for widget in self.manageModsFrame.winfo_children():

            widget.bind("<1>", lambda event: self.mainWindow.focus_set())

        for widget in self.exportModCanvasFrame.winfo_children():

            if widget.winfo_class() == "Entry":

                widget.bind("<1>", lambda event: widget.focus_set())

        self.config()

        self.mainWindow.mainloop()

    def createNewExportFile(self):

        if not hasattr(self, 'exportFileList'):

            self.exportFileList = []

        fileDir = askopenfilenames()

        for file in fileDir:

            item = PackFileWidget(self,
                                  self.exportModCanvasFrame.scrollable_frame,
                                  len(self.exportFileList), 0, "", file)

            self.exportFileList.append(item)

    def RemoveAllPackFiles(self):

        try:
            while len(self.exportFileList) != 0:

                self.exportFileList[0].destroy()
                self.exportModCanvasFrame.scrollable_frame.update()
                self.exportModCanvasFrame.canvas.update()
        except:
            pass

    def styles(self):

        self.style = ttk.Style(self.mainWindow)
        self.headerFont = font.Font(size=20, weight='bold')

    def config(self):

        self.configParser = configparser.ConfigParser()

        if not path.isfile("{}/config.ini".format(self.dataDir)):

            self.configParser["DEFAULT"] = {
                "GameDirectory": "",
                "version": 1.0
            }

            with open("{}/config.ini".format(self.dataDir), "w") as configfile:

                self.configParser.write(configfile)

                configfile.close()

        self.configParser = configparser.ConfigParser()

        self.configParser.read("{}/config.ini".format(self.dataDir))

        if not path.isdir(self.configParser["DEFAULT"]["GameDirectory"]):

            self.planetCoasterDir = messagebox.showinfo(
                "Welcome!",
                "Please select your Planet Coaster installation folder (usually found in the Steamgames folder)"
            )

            while True:

                self.planetCoasterDir = askdirectory()

                if self.planetCoasterDir.split("/")[-1] != "Planet Coaster":

                    messagebox.showerror(
                        "Error!", "Not a valid Planet Coaster directory!")

                else:
                    break

        else:

            self.planetCoasterDir = self.configParser["DEFAULT"][
                "GameDirectory"]

        self.configParser["DEFAULT"] = {
            "GameDirectory": self.planetCoasterDir,
            "version": self.version
        }

        with open("{}/config.ini".format(self.dataDir), "w") as configfile:
            self.configParser.write(configfile)

        self.backupDir = "{}/backups".format(self.planetCoasterDir)

        try:
            mkdir(self.backupDir)
        except:
            pass

        try:
            self.updater = Updater()

            if self.updater.check_update(self.version):
                print("updating...")
                if messagebox.askyesno(
                        "Info!",
                        "Update found to Version {} (current is Version {})\nUpdate Description: {}\n\nDo you want to visit the download page?"
                        .format(self.updater.get_tag(), self.version,
                                self.updater.get_desc())):
                    webbrowser.open(
                        "https://github.com/Distantz/Planet-Coaster-Mod-Loader/releases/tag/{}"
                        .format(self.updater.get_tag()),
                        new=0,
                        autoraise=True)

        except:
            messagebox.showinfo(
                "Updater Broke",
                "Updater could not ping github server so cannot check for update"
            )

    def pack(self):

        self.modName = (self.metaNameEntry.get()).strip()
        self.metaAuth = (self.metaAuthEntry.get()).strip()
        self.metaDesc = (self.metaDescEntry.get()).strip()

        outputDir = asksaveasfilename(title="Pick output folder",
                                      initialdir=dir_path,
                                      filetypes=[("PC Mod Package", "*.pcm")],
                                      initialfile=self.modName).replace(
                                          " ", "_")

        if outputDir == "":

            print("I dont think so")
            return

        self.Meta = pcm.meta(self.modName, self.metaAuth, self.metaDesc)

        self.out = {}
        self.out["Files"] = {}
        self.filesList = sorted(self.exportFileList,
                                key=lambda x: x.ovlFile,
                                reverse=True)
        for i, self.filePack in enumerate(self.filesList):
            self.OVLPath = self.filesList[i].entryVar.get()
            self.shortenedOVLPath = self.OVLPath[self.OVLPath.find("Win64"):]

            if self.shortenedOVLPath not in self.out["Files"]:
                self.out["Files"][self.shortenedOVLPath] = []

            self.shortenedOVLPath = self.shortenedOVLPath[self.shortenedOVLPath
                                                          .find("Win64"):]
            print(type(self.filePack.file))
            print(self.filePack.file)
            self.out["Files"][self.shortenedOVLPath].append(
                (self.filePack.file.rsplit("/", 1)[1]))

        self.saveDir = outputDir

        try:

            mkdir(self.saveDir)

            with open("{}/mod.json".format(self.saveDir), "w") as file:

                file.write("{}")

        except:

            raise ValueError

        self.Meta = pcm.meta(self.metaNameEntry.get(),
                             self.metaAuthEntry.get(),
                             self.metaDescEntry.get())
        self.PCM = pcm.pcm(self.out, self.Meta)
        self.PCM.write_meta()
        self.PCM.write_pcm()
        self.PCM.pcm_write_to_file(self.saveDir)

        for i, self.filePack in enumerate(self.filesList):

            self.OVLPath = self.filesList[i].entryVar.get()
            self.shortenedOVLPath = self.OVLPath[self.OVLPath.find("Win64"):]

            self.shortenedOVLPath = self.shortenedOVLPath[self.shortenedOVLPath
                                                          .find("Win64"):]
            self.dirName = self.shortenedOVLPath.replace("/", "_")
            self.dirName = self.dirName.replace(":", "#")
            print(self.saveDir + "/" + self.dirName)
            try:
                mkdir(self.saveDir + "/" + self.dirName)
            except:
                pass
            shutil.copyfile(
                self.filePack.file, self.saveDir + "/" + self.dirName + "/" +
                self.filePack.file.split("/")[-1])

        shutil.make_archive(self.saveDir, 'zip', self.saveDir)

        rename(self.saveDir + ".zip", self.saveDir + ".pcm")
        shutil.rmtree(self.saveDir)

        messagebox.showinfo(
            "Info!",
            "The mod was successfully exported to the directory: {}".format(
                self.saveDir + ".pcm"))

    def inject_mod(self, filepath):

        if filepath != "":

            try:

                self.modToBeInjected = Mod(self)
                self.modToBeInjected.loadMeta(filepath)

                if (any(x.modName == self.modToBeInjected.modName
                        for x in self.modList)) == False:
                    self.modToBeInjected.install(filepath)
                    self.modToBeInjected.save()
                    self.modList.append(self.modToBeInjected)
                    widget = ModTileWidget(
                        self.modToBeInjected, self,
                        self.manageModsScrollFrame.scrollable_frame,
                        self.modList.index(self.modToBeInjected), 0)
                    self.modWidgetList.append(widget)

                else:
                    messagebox.showinfo(
                        "Information",
                        "Mod with this name is already installed, try remove any mods with the same name!"
                    )

                    #Add in option to continue?
                    #> Nah bro i'm lazy

            except Exception as error:
                print(error)
                messagebox.showerror("Error!",
                                     "The selected mod failed to install.")

    def restore(self):

        print("In Restore")

        for i in range(len(self.modWidgetList)):

            print(i)
            self.modWidgetList[i].destroy()

    def loadModsList(self):
        self.tempList = []
        with open(self.modsJsonPath, "r") as file:
            self.fileData = file.read()
            if len(self.fileData) == 0:
                return self.tempList
            self.modData = loads(self.fileData)
            for self.mod in self.modData:
                self.newMod = Mod(self)
                self.newMod.modName = self.modData[self.mod]["Name"]
                self.newMod.modAuthor = self.modData[self.mod]["Author"]
                self.newMod.modDesc = self.modData[self.mod]["Desc"]
                self.newMod.backupPaths = self.modData[self.mod]["Backups"]
                self.newMod.OVLs = self.modData[self.mod]["OVLs"]
                self.tempList.append(self.newMod)
        return self.tempList