diff -Nru playonlinux-4.3.4/debian/changelog playonlinux-4.3.4/debian/changelog --- playonlinux-4.3.4/debian/changelog 2020-03-31 18:44:40.000000000 +0000 +++ playonlinux-4.3.4/debian/changelog 2020-05-11 15:48:43.000000000 +0000 @@ -1,14 +1,15 @@ -playonlinux (4.3.4-1ubuntu1) focal; urgency=medium +playonlinux (4.3.4-2) unstable; urgency=medium - * Build-depend on python-all. + * Team upload. - -- Matthias Klose Tue, 31 Mar 2020 20:44:40 +0200 + [ Scott Talbert ] + * Port to Python 3. (Closes: #937302) -playonlinux (4.3.4-1build1) focal; urgency=medium + [ Markus Koschany ] + * Switch to debhelper-compat = 13. + * Declare compliance with Debian Policy 4.5.0. - * No-change rebuild to generate dependencies on python2. - - -- Matthias Klose Tue, 17 Dec 2019 12:39:26 +0000 + -- Markus Koschany Mon, 11 May 2020 17:48:43 +0200 playonlinux (4.3.4-1) unstable; urgency=medium diff -Nru playonlinux-4.3.4/debian/compat playonlinux-4.3.4/debian/compat --- playonlinux-4.3.4/debian/compat 2017-07-09 17:28:28.000000000 +0000 +++ playonlinux-4.3.4/debian/compat 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -9 diff -Nru playonlinux-4.3.4/debian/control playonlinux-4.3.4/debian/control --- playonlinux-4.3.4/debian/control 2020-03-31 18:44:13.000000000 +0000 +++ playonlinux-4.3.4/debian/control 2020-05-11 15:48:43.000000000 +0000 @@ -3,8 +3,8 @@ Priority: optional Maintainer: Debian Games Team Uploaders: Bertrand Marc -Build-Depends: debhelper (>= 9), dh-python, python-all, imagemagick -Standards-Version: 4.3.0 +Build-Depends: debhelper-compat (= 13), dh-python, python3, imagemagick +Standards-Version: 4.5.0 Vcs-Git: https://salsa.debian.org/games-team/playonlinux.git Vcs-Browser: https://salsa.debian.org/games-team/playonlinux Homepage: https://www.playonlinux.com/ @@ -12,7 +12,7 @@ Package: playonlinux Architecture: all Depends: ${misc:Depends}, - ${python:Depends}, + ${python3:Depends}, binutils, bzip2, cabextract, @@ -25,7 +25,8 @@ mesa-utils, netcat, p7zip-full, - python-wxgtk3.0, + python3-natsort, + python3-wxgtk4.0 (>= 4.0.7+dfsg-3~), unzip, wget, wine32|wine, diff -Nru playonlinux-4.3.4/debian/patches/python3.patch playonlinux-4.3.4/debian/patches/python3.patch --- playonlinux-4.3.4/debian/patches/python3.patch 1970-01-01 00:00:00.000000000 +0000 +++ playonlinux-4.3.4/debian/patches/python3.patch 2020-05-11 15:48:43.000000000 +0000 @@ -0,0 +1,2097 @@ +Description: Port playonlinux to Python 3 +Author: Scott Talbert +Forwarded: no +--- a/python/configurewindow/ConfigureWindow.py ++++ b/python/configurewindow/ConfigureWindow.py +@@ -1,4 +1,4 @@ +-#!/usr/bin/env python ++#!/usr/bin/python3 + # -*- coding: utf-8 -*- + + # Copyright (C) 2009 Pâris Quentin +@@ -71,8 +71,8 @@ class ConfigureWindow(wx.Frame): + self.AddPrefix = wx.Button(self.prefixPanel, 1001, _("New"), pos=(0, 0), size=(95, 25)) + self.DelPrefix = wx.Button(self.prefixPanel, 1002, _("Remove"), pos=(100, 0), size=(95, 25)) + +- wx.EVT_BUTTON(self, 1001, self.NewPrefix) +- wx.EVT_BUTTON(self, 1002, self.DeletePrefix) ++ self.Bind(wx.EVT_BUTTON, self.NewPrefix, id=1001) ++ self.Bind(wx.EVT_BUTTON, self.DeletePrefix, id=1002) + + + self.list_game.SetSpacing(0) +@@ -91,7 +91,7 @@ class ConfigureWindow(wx.Frame): + self.list_software() + + self.onglets.panelGeneral.Bind(wx.EVT_LEFT_UP, self.onglets.ReleaseTyping) +- wx.EVT_TREE_SEL_CHANGED(self, 900, self.change_program_to_selection) ++ self.Bind(wx.EVT_TREE_SEL_CHANGED, self.change_program_to_selection, id=900) + + self.timer = wx.Timer(self, 1) + self.Bind(wx.EVT_TIMER, self.AutoReload, self.timer) +@@ -112,15 +112,15 @@ class ConfigureWindow(wx.Frame): + if(self.onglets.s_prefix == "default"): + wx.MessageBox(_("This virtual drive is protected"), os.environ["APPLICATION_TITLE"]) + else: +- if(wx.YES == wx.MessageBox(_("Are you sure you want to delete {0} virtual drive ?").format(self.onglets.s_prefix.encode("utf-8","replace")).decode("utf-8","replace"), os.environ["APPLICATION_TITLE"], style=wx.YES_NO | wx.ICON_QUESTION)): ++ if(wx.YES == wx.MessageBox(_("Are you sure you want to delete {0} virtual drive ?").format(self.onglets.s_prefix), os.environ["APPLICATION_TITLE"], style=wx.YES_NO | wx.ICON_QUESTION)): + mylist = os.listdir(Variables.playonlinux_rep+"/shortcuts") + for element in mylist: + if(playonlinux.getPrefix(element).lower() == self.onglets.s_prefix.lower()): + subprocess.Popen(["bash", Variables.playonlinux_env+"/bash/uninstall", "--non-interactive", element]) + self._delete_directory(Variables.playonlinux_rep+"/wineprefix/"+self.onglets.s_prefix) + else: +- if(wx.YES == wx.MessageBox(_("Are you sure you want to delete {0} ?").format(self.onglets.s_title.encode("utf-8","replace")).decode("utf-8","replace"), os.environ["APPLICATION_TITLE"], style=wx.YES_NO | wx.ICON_QUESTION)): +- subprocess.Popen(["bash", Variables.playonlinux_env+"/bash/uninstall", "--non-interactive", self.onglets.s_title.encode('utf-8', 'replace')]) ++ if(wx.YES == wx.MessageBox(_("Are you sure you want to delete {0} ?").format(self.onglets.s_title), os.environ["APPLICATION_TITLE"], style=wx.YES_NO | wx.ICON_QUESTION)): ++ subprocess.Popen(["bash", Variables.playonlinux_env+"/bash/uninstall", "--non-interactive", self.onglets.s_title]) + + self.onglets.s_isPrefix = True + self.change_program("default",True) +@@ -150,7 +150,7 @@ class ConfigureWindow(wx.Frame): + # To speed up the process, only modify metadata when necessary + attr = os.lstat(fullpath) + if attr.st_mode & needed_dir_rights != needed_dir_rights: +- print "%s rights need fixing" % fullpath ++ print("%s rights need fixing" % fullpath) + os.chmod(fullpath, needed_dir_rights) + + # Alright, now we should be able to proceed +@@ -295,9 +295,9 @@ class ConfigureWindow(wx.Frame): + self.list_game.ExpandAll() + try: + if(self.onglets.s_isPrefix == True): +- self.list_game.SelectItem(self.prefixes_item[self.onglets.s_prefix.encode("utf-8","replace")]) ++ self.list_game.SelectItem(self.prefixes_item[self.onglets.s_prefix]) + else: +- self.list_game.SelectItem(self.games_item[self.onglets.s_title.encode("utf-8","replace")]) ++ self.list_game.SelectItem(self.games_item[self.onglets.s_title]) + except: + self.onglets.s_isPrefix = True + self.change_program("default",True) +@@ -308,3 +308,7 @@ class ConfigureWindow(wx.Frame): + + def apply_settings(self, event): + self.Destroy() ++ ++ def Destroy(self): ++ self.timer.Stop() ++ return super().Destroy() +--- a/python/configurewindow/PackageList.py ++++ b/python/configurewindow/PackageList.py +@@ -44,7 +44,7 @@ class PackageList: + broken = True + break + +- except IndexError, e: # Index error : There is no ':' in the line, so the content of the line is the package we want to install. No need to continue ++ except IndexError as e: # Index error : There is no ':' in the line, so the content of the line is the package we want to install. No need to continue + broken = True + break + +@@ -56,6 +56,6 @@ class PackageList: + def getNameFromPackageLine(package): + try: + realName = package.split(":")[1].replace("POL_Install_", "") +- except IndexError, e: ++ except IndexError as e: + realName = package.replace("POL_Install_", "") +- return realName +\ No newline at end of file ++ return realName +--- a/python/debug.py ++++ b/python/debug.py +@@ -1,4 +1,4 @@ +-#!/usr/bin/env python ++#!/usr/bin/python3 + # -*- coding: utf-8 -*- + + # Copyright (C) 2009 Pâris Quentin +@@ -57,7 +57,7 @@ class MainWindow(wx.Frame): + self.images = wx.ImageList(16, 16) + + self.list_game = wx.TreeCtrl(self.splitter, 900, size = wx.DefaultSize, style=wx.TR_HIDE_ROOT) +- wx.EVT_TREE_SEL_CHANGED(self, 900, self.analyseLog) ++ self.Bind(wx.EVT_TREE_SEL_CHANGED, self.analyseLog, id=900) + + + self.list_game.SetSpacing(0); +@@ -85,8 +85,9 @@ class MainWindow(wx.Frame): + self.HideLogFile() + else: + self.analyseReal(logtype,logcheck) +- wx.EVT_BUTTON(self,101,self.locate) +- wx.EVT_BUTTON(self,102,self.bugReport) ++ self.Bind(wx.EVT_BUTTON,self.locate,id=101) ++ self.Bind(wx.EVT_BUTTON,self.bugReport,id=102) ++ self.Bind(wx.EVT_CLOSE,self.app_Close) + + #self.log_reader.SetDefaultStyle(wx.TextAttr(font=wx.Font(13,wx.FONTFAMILY_DEFAULT,wx.FONTSTYLE_NORMAL,wx.FONTWEIGHT_NORMAL))) + +@@ -124,7 +125,7 @@ class MainWindow(wx.Frame): + line=line[0:200] + leng=200 + +- self.log_reader.AppendText(line.decode('utf-8','replace')) ++ self.log_reader.AppendText(line) + + self.bold = wx.Font(wx.NORMAL_FONT.GetPointSize(), wx.FONTFAMILY_DEFAULT, wx.NORMAL, wx.BOLD) + +@@ -195,7 +196,7 @@ class MainWindow(wx.Frame): + + def OnFocus(self, event): + if self.need_redisplay: +- print 'Need to redisplay log' ++ print('Need to redisplay log') + self.initLogDisplay() + + def analyseLog(self, event): +@@ -326,3 +327,7 @@ class MainWindow(wx.Frame): + + def apply_settings(self, event): + self.Destroy() ++ ++ def Destroy(self): ++ self.timer.Stop() ++ return super().Destroy() +--- a/python/lib/Variables.py ++++ b/python/lib/Variables.py +@@ -1,8 +1,8 @@ +-#!/usr/bin/env python ++#!/usr/bin/python3 + # Copyright (C) 2007-2010 PlayOnLinux Team + # Copyright (C) 2011 - Quentin PARIS + +-import os, random, sys, string ++import os, random, sys + import wx, lib.playonlinux as playonlinux + from lib.dpiFetcher import dpiFetcher + +@@ -10,7 +10,7 @@ from lib.dpiFetcher import dpiFetcher + try: + os.environ["POL_OS"] + except: +- print "ERROR ! Please define POL_OS environment var first." ++ print("ERROR ! Please define POL_OS environment var first.") + os._exit(1) + + # Variables mixte 1 +@@ -84,7 +84,7 @@ if os.environ["POL_OS"] == "FreeBSD": + + os.environ["POL_CURL"] = "curl" + +-archi = string.split(os.environ["MACHTYPE"], "-") ++archi = os.environ["MACHTYPE"].split("-") + archi = archi[0] + + if archi == "x86_64": +--- a/python/lib/playonlinux.py ++++ b/python/lib/playonlinux.py +@@ -1,10 +1,12 @@ +-#!/usr/bin/env python ++#!/usr/bin/python3 + # -*- coding: utf-8 -*- + + # Copyright (C) 2007-2010 PlayOnLinux Team + +-import Variables, os, string ++from . import Variables ++import os + import subprocess, shlex, pipes, wx ++import natsort + + def winpath(script, path): + #path=os.path.realpath(path) +@@ -27,7 +29,7 @@ def open_document(path, ext): + wx.MessageBox(_("There is nothing installed to run .{0} files.").format(ext),os.environ["APPLICATION_TITLE"], wx.OK) + else: + try: +- subprocess.Popen(["bash", Variables.playonlinux_env+"/bash/run_app", script.encode("utf-8","replace"), winpath(script.encode("utf-8","replace"), path.encode("utf-8","replace"))]) ++ subprocess.Popen(["bash", Variables.playonlinux_env+"/bash/run_app", script, winpath(script, path)]) + except: + subprocess.Popen(["bash", Variables.playonlinux_env+"/bash/run_app", script, winpath(script, path)]) + +@@ -46,7 +48,7 @@ def GetWineVersion(game): + version = "System" + else: + version=line.replace("PATH=","").replace("\"","").replace(Variables.playonlinux_rep,"").replace("//","/") +- version = string.split(version,"/") ++ version = version.split("/") + version = version[1] + + return(version) +@@ -73,9 +75,9 @@ def GetSettings(setting, prefix='_POL_') + break + i += 1 + try: +- line = string.split(line,"=") ++ line = line.split("=") + del line[0] +- return(string.join(line,"=")) ++ return("=".join(line)) + except: + return("") + +@@ -152,7 +154,7 @@ def getLog(game): + for line in fichier: + line = line.replace("\n","") + if('#POL_Log=' in line): +- line = string.split(line,"=") ++ line = line.split("=") + return(line[1]) + return None + +@@ -255,8 +257,8 @@ def open_folder_prefix(software): + subprocess.call(["xdg-open", AppDir]) + + def VersionLower(version1, version2): +- version1 = string.split(version1, "-") +- version2 = string.split(version2, "-") ++ version1 = version1.split("-") ++ version2 = version2.split("-") + + try: + if(version1[1] != ""): +@@ -276,11 +278,11 @@ def VersionLower(version1, version2): + else: + return False + +- version1 = [ int(digit) for digit in string.split(version1[0],".") ] ++ version1 = [ int(digit) for digit in version1[0].split(".") ] + while len(version1) < 3: + version1.append(0) + +- version2 = [ int(digit) for digit in string.split(version2[0],".") ] ++ version2 = [ int(digit) for digit in version2[0].split(".") ] + while len(version2) < 3: + version2.append(0) + +@@ -303,15 +305,15 @@ def convertVersionToInt(version): # Code + #rajouter pour les vesions de dev -> la version stable peut sortir + #les personnes qui utilise la version de dev sont quand même informé d'une MAJ + #ex 3.8.1 < 3.8.2-dev < 3.8.2 +- print "Deprecated !" ++ print("Deprecated !") + if("dev" in version or "beta" in version or "alpha" in version or "rc" in version): +- version = string.split(version,"-") ++ version = version.split("-") + version = version[0] + versionDev = -5 + else: + versionDev = 0 + +- version_s = string.split(version,".") ++ version_s = version.split(".") + #on fait des maths partie1 elever au cube et multiplier par 1000 + try: + versionP1 = int(version_s[0])*int(version_s[0])*int(version_s[0])*1000 +@@ -332,7 +334,7 @@ def getPrefix(shortcut): # Get prefix na + return "" + + fichier = open(os.environ["POL_USER_ROOT"]+"/shortcuts/"+shortcut,'r').read() +- fichier = string.split(fichier,"\n") ++ fichier = fichier.split("\n") + i = 0 + while(i < len(fichier)): + if("export WINEPREFIX=" in fichier[i]): +@@ -340,9 +342,9 @@ def getPrefix(shortcut): # Get prefix na + i += 1 + + try: +- prefix = string.split(fichier[i],"\"") ++ prefix = fichier[i].split("\"") + prefix = prefix[1].replace("//","/") +- prefix = string.split(prefix,"/") ++ prefix = prefix.split("/") + + if(os.environ["POL_OS"] == "Mac"): + index_of_dotPOL = prefix.index("PlayOnMac") +@@ -361,7 +363,7 @@ def getArgs(shortcut): # Get prefix name + return "" + + fichier = open(os.environ["POL_USER_ROOT"]+"/shortcuts/"+shortcut,'r').read() +- fichier = string.split(fichier,"\n") ++ fichier = fichier.split("\n") + i = 0 + while(i < len(fichier)): + if("POL_Wine " in fichier[i]): +@@ -380,7 +382,7 @@ def getArgs(shortcut): # Get prefix name + + def Get_versions(arch='x86'): + installed_versions = os.listdir(Variables.playonlinux_rep+"/wine/"+Variables.os_name+"-"+arch+"/") +- installed_versions.sort(key=keynat) ++ installed_versions.sort(key=natsort.natsort_keygen()) + installed_versions.reverse() + try: + installed_versions.remove("installed") +--- a/python/mainwindow.py ++++ b/python/mainwindow.py +@@ -1,4 +1,4 @@ +-#!/usr/bin/env python ++#!/usr/bin/python3 + # -*- coding: utf-8 -*- + + # Copyright (C) 2008 Pâris Quentin +@@ -22,26 +22,20 @@ encoding = 'utf-8' + import os + import shlex + import signal +-import string + import subprocess + import sys + import time +-import urllib ++import urllib.parse + import webbrowser + + try: + os.environ["POL_OS"] + except: +- print "ERROR ! Please define POL_OS environment var first." ++ print("ERROR ! Please define POL_OS environment var first.") + os._exit(1) + +-if (os.environ["POL_OS"] != "Mac"): +- import wxversion +- +- wxversion.ensureMinimal('2.8') +- +-import wx, wx.aui +-import wx.lib.hyperlink ++import wx, wx.aui, wx.adv ++import wx.lib.agw.hyperlink + import lib.lng as lng + import lib.playonlinux as playonlinux, lib.Variables as Variables + import options, threading, debug +@@ -98,7 +92,7 @@ class POLWeb(threading.Thread): + self.updating = True + exe = ['bash', Variables.playonlinux_env + "/bash/pol_update_list"] + +- p = subprocess.Popen(exe, stdout=subprocess.PIPE, bufsize=1, ++ p = subprocess.Popen(exe, stdout=subprocess.PIPE, bufsize=1, universal_newlines=True, + preexec_fn=lambda: os.setpgid(os.getpid(), os.getpid())) + + for line in iter(p.stdout.readline, ''): +@@ -342,8 +336,8 @@ class MainWindow(wx.Frame): + self.bitmap = wx.Bitmap(Variables.playonlinux_env + "/etc/playonlinux16.png") + + self.plugin_item.SetBitmap(self.bitmap) +- self.pluginsmenu.AppendItem(self.plugin_item) +- wx.EVT_MENU(self, 300 + self.j, self.run_plugin) ++ self.pluginsmenu.Append(self.plugin_item) ++ self.Bind(wx.EVT_MENU, self.run_plugin, id=300 + self.j) + self.plugin_list.append(files[self.i]) + self.j += 1 + self.i += 1 +@@ -354,7 +348,7 @@ class MainWindow(wx.Frame): + self.option_item_p = wx.MenuItem(self.pluginsmenu, 214, _("Plugin manager")) + self.option_item_p.SetBitmap(wx.Bitmap(Variables.playonlinux_env + "/etc/onglet/package-x-generic.png")) + +- self.pluginsmenu.AppendItem(self.option_item_p) ++ self.pluginsmenu.Append(self.option_item_p) + + self.last_string = "" + +@@ -392,18 +386,18 @@ class MainWindow(wx.Frame): + self.toolbar = self.CreateToolBar(wx.TB_TEXT) + self.toolbar.SetToolBitmapSize(iconSize) + self.searchbox = wx.SearchCtrl(self.toolbar, 124, style=wx.RAISED_BORDER) +- self.playTool = self.toolbar.AddLabelTool(wx.ID_OPEN, _("Run"), wx.Bitmap( ++ self.playTool = self.toolbar.AddTool(wx.ID_OPEN, _("Run"), wx.Bitmap( + Variables.playonlinux_env + "/resources/images/toolbar/play.png")) +- self.stopTool = self.toolbar.AddLabelTool(123, _("Close"), wx.Bitmap( ++ self.stopTool = self.toolbar.AddTool(123, _("Close"), wx.Bitmap( + Variables.playonlinux_env + "/resources/images/toolbar/stop.png")) + + self.toolbar.AddSeparator() +- self.toolbar.AddLabelTool(wx.ID_ADD, _("Install"), ++ self.toolbar.AddTool(wx.ID_ADD, _("Install"), + wx.Bitmap(Variables.playonlinux_env + "/resources/images/toolbar/install.png")) +- self.removeTool = self.toolbar_remove = self.toolbar.AddLabelTool(wx.ID_DELETE, _("Remove"), wx.Bitmap( ++ self.removeTool = self.toolbar_remove = self.toolbar.AddTool(wx.ID_DELETE, _("Remove"), wx.Bitmap( + Variables.playonlinux_env + "/resources/images/toolbar/delete.png")) + self.toolbar.AddSeparator() +- self.toolbar.AddLabelTool(121, _("Configure"), ++ self.toolbar.AddTool(121, _("Configure"), + wx.Bitmap(Variables.playonlinux_env + "/resources/images/toolbar/configure.png")) + + try: +@@ -424,53 +418,53 @@ class MainWindow(wx.Frame): + + self.toolbar.Realize() + self.Reload(self) +- wx.EVT_MENU(self, wx.ID_OPEN, self.Run) +- wx.EVT_MENU(self, 123, self.RKill) ++ self.Bind(wx.EVT_MENU, self.Run, id=wx.ID_OPEN) ++ self.Bind(wx.EVT_MENU, self.RKill, id=123) + +- wx.EVT_MENU(self, wx.ID_ADD, self.InstallMenu) +- wx.EVT_MENU(self, wx.ID_ABOUT, self.About) +- wx.EVT_MENU(self, wx.ID_EXIT, self.ClosePol) +- wx.EVT_MENU(self, wx.ID_DELETE, self.UninstallGame) ++ self.Bind(wx.EVT_MENU, self.InstallMenu, id=wx.ID_ADD) ++ self.Bind(wx.EVT_MENU, self.About, id=wx.ID_ABOUT) ++ self.Bind(wx.EVT_MENU, self.ClosePol, id=wx.ID_EXIT) ++ self.Bind(wx.EVT_MENU, self.UninstallGame, id=wx.ID_DELETE) + + # Display +- wx.EVT_MENU(self, 501, self.iconDisplay) +- wx.EVT_MENU(self, 502, self.iconDisplay) +- wx.EVT_MENU(self, 503, self.iconDisplay) +- wx.EVT_MENU(self, 504, self.iconDisplay) ++ self.Bind(wx.EVT_MENU, self.iconDisplay, id=501) ++ self.Bind(wx.EVT_MENU, self.iconDisplay, id=502) ++ self.Bind(wx.EVT_MENU, self.iconDisplay, id=503) ++ self.Bind(wx.EVT_MENU, self.iconDisplay, id=504) + + # Expert +- wx.EVT_MENU(self, 101, self.Reload) +- wx.EVT_MENU(self, 107, self.WineVersion) +- wx.EVT_MENU(self, 108, self.Executer) +- wx.EVT_MENU(self, 109, self.PolShell) +- wx.EVT_MENU(self, 110, self.BugReport) +- wx.EVT_MENU(self, 112, self.POLOnline) +- wx.EVT_MENU(self, 113, self.PCCd) +- wx.EVT_MENU(self, 115, self.killall) +- wx.EVT_MENU(self, 121, self.Configure) +- wx.EVT_MENU(self, 122, self.Package) +- wx.EVT_TEXT(self, 124, self.Reload) ++ self.Bind(wx.EVT_MENU, self.Reload, id=101) ++ self.Bind(wx.EVT_MENU, self.WineVersion, id=107) ++ self.Bind(wx.EVT_MENU, self.Executer, id=108) ++ self.Bind(wx.EVT_MENU, self.PolShell, id=109) ++ self.Bind(wx.EVT_MENU, self.BugReport, id=110) ++ self.Bind(wx.EVT_MENU, self.POLOnline, id=112) ++ self.Bind(wx.EVT_MENU, self.PCCd, id=113) ++ self.Bind(wx.EVT_MENU, self.killall, id=115) ++ self.Bind(wx.EVT_MENU, self.Configure, id=121) ++ self.Bind(wx.EVT_MENU, self.Package, id=122) ++ self.Bind(wx.EVT_TEXT, self.Reload, id=124) + + # Options +- wx.EVT_MENU(self, 210, self.Options) +- wx.EVT_MENU(self, 211, self.Options) +- wx.EVT_MENU(self, 212, self.Options) +- wx.EVT_MENU(self, 213, self.Options) +- wx.EVT_MENU(self, 214, self.Options) +- wx.EVT_MENU(self, 215, self.Options) +- +- wx.EVT_MENU(self, 216, self.donate) +- +- wx.EVT_CLOSE(self, self.ClosePol) +- wx.EVT_TREE_ITEM_ACTIVATED(self, 105, self.Run) +- wx.EVT_TREE_SEL_CHANGED(self, 105, self.Select) ++ self.Bind(wx.EVT_MENU, self.Options, id=210) ++ self.Bind(wx.EVT_MENU, self.Options, id=211) ++ self.Bind(wx.EVT_MENU, self.Options, id=212) ++ self.Bind(wx.EVT_MENU, self.Options, id=213) ++ self.Bind(wx.EVT_MENU, self.Options, id=214) ++ self.Bind(wx.EVT_MENU, self.Options, id=215) ++ ++ self.Bind(wx.EVT_MENU, self.donate, id=216) ++ ++ self.Bind(wx.EVT_CLOSE, self.ClosePol) ++ self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.Run, id=105) ++ self.Bind(wx.EVT_TREE_SEL_CHANGED, self.Select, id=105) + + # Support +- wx.EVT_MENU(self, 400, self.runSupport) +- wx.EVT_MENU(self, 401, self.runSupport) +- wx.EVT_MENU(self, 402, self.runSupport) +- wx.EVT_MENU(self, 403, self.runSupport) +- wx.EVT_MENU(self, 404, self.runSupport) ++ self.Bind(wx.EVT_MENU, self.runSupport, id=400) ++ self.Bind(wx.EVT_MENU, self.runSupport, id=401) ++ self.Bind(wx.EVT_MENU, self.runSupport, id=402) ++ self.Bind(wx.EVT_MENU, self.runSupport, id=403) ++ self.Bind(wx.EVT_MENU, self.runSupport, id=404) + + # PlayOnLinux main timer + self.timer = wx.Timer(self, 1) +@@ -487,14 +481,14 @@ class MainWindow(wx.Frame): + self.SetupWindowTimer_delay = 100 + + # Pop-up menu for game list: beginning +- wx.EVT_TREE_ITEM_MENU(self, 105, self.RMBInGameList) +- wx.EVT_MENU(self, 230, self.RWineConfigurator) +- wx.EVT_MENU(self, 231, self.RRegistryEditor) +- wx.EVT_MENU(self, 232, self.GoToAppDir) +- wx.EVT_MENU(self, 233, self.ChangeIcon) +- wx.EVT_MENU(self, 234, self.UninstallGame) +- wx.EVT_MENU(self, 235, self.RKill) +- wx.EVT_MENU(self, 236, self.ReadMe) ++ self.Bind(wx.EVT_TREE_ITEM_MENU, self.RMBInGameList, id=105) ++ self.Bind(wx.EVT_MENU, self.RWineConfigurator, id=230) ++ self.Bind(wx.EVT_MENU, self.RRegistryEditor, id=231) ++ self.Bind(wx.EVT_MENU, self.GoToAppDir, id=232) ++ self.Bind(wx.EVT_MENU, self.ChangeIcon, id=233) ++ self.Bind(wx.EVT_MENU, self.UninstallGame, id=234) ++ self.Bind(wx.EVT_MENU, self.RKill, id=235) ++ self.Bind(wx.EVT_MENU, self.ReadMe, id=236) + self.Bind(wx.EVT_SIZE, self.ResizeWindow) + self._mgr.restorePosition() + +@@ -571,7 +565,7 @@ class MainWindow(wx.Frame): + self.sb.Hide() + self.installFrame.setWaitState(False) + self.installFrame.Refresh() +- except wx._core.PyDeadObjectError: ++ except RuntimeError: + pass + except AttributeError: # FIXME: Install Frame is not opened + pass +@@ -867,11 +861,11 @@ class MainWindow(wx.Frame): + pass + + if (url == None): +- self.menuElem[id] = wx.lib.hyperlink.HyperLinkCtrl(self.menu_gauche, 10000 + pos, text, ++ self.menuElem[id] = wx.lib.agw.hyperlink.HyperLinkCtrl(self.menu_gauche, 10000 + pos, text, + pos=(35, 15 + pos * 20)) + self.menuElem[id].AutoBrowse(False) + else: +- self.menuElem[id] = wx.lib.hyperlink.HyperLinkCtrl(self.menu_gauche, 10000 + pos, text, ++ self.menuElem[id] = wx.lib.agw.hyperlink.HyperLinkCtrl(self.menu_gauche, 10000 + pos, text, + pos=(35, 15 + pos * 20)) + self.menuElem[id].setURL(url) + +@@ -883,7 +877,7 @@ class MainWindow(wx.Frame): + # self.menuElem[id].SetHoverColour(wx.Colour(100,100,100)) + + if (evt != None): +- wx.lib.hyperlink.EVT_HYPERLINK_LEFT(self, 10000 + pos, evt) ++ self.Bind(wx.lib.agw.hyperlink.EVT_HYPERLINK_LEFT, evt, id=10000 + pos) + + def donate(self, event): + if (os.environ["POL_OS"] == "Mac"): +@@ -909,7 +903,7 @@ class MainWindow(wx.Frame): + else: + self.iconFolder = "full_size" + for game in self.games: # METTRE EN 32x32 +- if (self.searchbox.GetValue().encode("utf-8", "replace").lower() in game.lower()): ++ if (self.searchbox.GetValue().lower() in game.lower()): + if (not os.path.isdir(Variables.playonlinux_rep + "/shortcuts/" + game)): + if (os.path.exists(Variables.playonlinux_rep + "/icones/" + self.iconFolder + "/" + game)): + file_icone = Variables.playonlinux_rep + "/icones/" + self.iconFolder + "/" + game +@@ -997,7 +991,7 @@ class MainWindow(wx.Frame): + os.environ["APPLICATION_TITLE"]), "default", True) + else: + self.configureFrame = ConfigureWindow.ConfigureWindow(self, -1, _("{0} configuration").format( +- os.environ["APPLICATION_TITLE"]), game_exec.decode("utf-8", "replace"), False) ++ os.environ["APPLICATION_TITLE"]), game_exec, False) + + self.configureFrame.Center(wx.BOTH) + self.configureFrame.Show(True) +@@ -1043,7 +1037,7 @@ class MainWindow(wx.Frame): + self.wversion.Show(True) + + def GetSelectedProgram(self): +- return self.list_game.GetItemText(self.list_game.GetSelection()).encode("utf-8", "replace") ++ return self.list_game.GetItemText(self.list_game.GetSelection()) + + def Run(self, event, s_debug=False): + +@@ -1086,7 +1080,7 @@ class MainWindow(wx.Frame): + game_log = str(playonlinux.getLog(game_exec)) + if game_log: + playonlinux.POL_Open( +- "http://www." + os.environ["POL_DNS"] + "/repository/feedback.php?script=" + urllib.quote_plus( ++ "http://www." + os.environ["POL_DNS"] + "/repository/feedback.php?script=" + urllib.parse.quote_plus( + game_log)) + + def POLDie(self): +@@ -1116,7 +1110,7 @@ class MainWindow(wx.Frame): + os._exit(63) # Restart code + + def ForceClose(self, signal, frame): # Catch SIGINT +- print "\nCtrl+C pressed. Killing all processes..." ++ print("\nCtrl+C pressed. Killing all processes...") + self.POLDie() + + def ClosePol(self, event): +@@ -1128,13 +1122,12 @@ class MainWindow(wx.Frame): + pids.append(pid) + except OSError: + pid_exists = False +- print "Registered PID: %d (%s)" % (pid, 'Present' if pid_exists else 'Missing') ++ print("Registered PID: %d (%s)" % (pid, 'Present' if pid_exists else 'Missing')) + self.registeredPid = pids + + if (playonlinux.GetSettings( + "DONT_ASK_BEFORE_CLOSING") == "TRUE" or self.registeredPid == [] or wx.YES == wx.MessageBox( +- _('Are you sure you want to close all {0} windows?').format(os.environ["APPLICATION_TITLE"]).decode( +- "utf-8", "replace"), os.environ["APPLICATION_TITLE"], style=wx.YES_NO | wx.ICON_QUESTION)): ++ _('Are you sure you want to close all {0} windows?').format(os.environ["APPLICATION_TITLE"]), os.environ["APPLICATION_TITLE"], style=wx.YES_NO | wx.ICON_QUESTION)): + self.SizeToSave = self.GetSize() + self.PositionToSave = self.GetPosition() + # Save size and position +@@ -1149,7 +1142,7 @@ class MainWindow(wx.Frame): + return None + + def About(self, event): +- self.aboutBox = wx.AboutDialogInfo() ++ self.aboutBox = wx.adv.AboutDialogInfo() + if (os.environ["POL_OS"] != "Mac"): + self.aboutBox.SetIcon(wx.Icon(Variables.playonlinux_env + "/etc/playonlinux.png", wx.BITMAP_TYPE_ANY)) + +@@ -1170,7 +1163,12 @@ class MainWindow(wx.Frame): + self.aboutBox.SetWebSite("http://www.playonmac.com") + else: + self.aboutBox.SetWebSite("http://www.playonlinux.com") +- wx.AboutBox(self.aboutBox) ++ wx.adv.AboutBox(self.aboutBox) ++ ++ def Destroy(self): ++ self.timer.Stop() ++ self.SetupWindowTimer.Stop() ++ return super().Destroy() + + + class PlayOnLinuxApp(wx.App): +@@ -1296,7 +1294,7 @@ class PlayOnLinuxApp(wx.App): + if (playonlinux.GetSettings("SEND_REPORT") == ""): + if (wx.YES == wx.MessageBox(_( + 'Do you want to help {0} to make a compatibility database?\n\nIf you click yes, the following things will be sent to us anonymously the first time you run a Windows program:\n\n- Your graphic card model\n- Your OS version\n- If graphic drivers are installed or not.\n\n\nThese information will be very precious for us to help people.').format( +- os.environ["APPLICATION_TITLE"]).decode("utf-8", "replace"), os.environ["APPLICATION_TITLE"], ++ os.environ["APPLICATION_TITLE"]), os.environ["APPLICATION_TITLE"], + style=wx.YES_NO | wx.ICON_QUESTION)): + playonlinux.SetSettings("SEND_REPORT", "TRUE") + else: +@@ -1333,7 +1331,7 @@ class PlayOnLinuxApp(wx.App): + pass + + def MacOpenFile(self, filename): +- file_extension = string.split(filename, ".") ++ file_extension = filename.split(".") + file_extension = file_extension[len(file_extension) - 1] + if (file_extension == "desktop"): # Un raccourcis Linux + content = open(filename, "r").readlines() +@@ -1358,8 +1356,7 @@ class PlayOnLinuxApp(wx.App): + + elif (file_extension == "pol" or file_extension == "POL"): + if (wx.YES == wx.MessageBox( +- _('Are you sure you want to want to install {0} package?').format(filename).decode("utf-8", +- "replace"), ++ _('Are you sure you want to want to install {0} package?').format(filename), + os.environ["APPLICATION_TITLE"], style=wx.YES_NO | wx.ICON_QUESTION)): + subprocess.Popen(["bash", Variables.playonlinux_env + "/bash/playonlinux-pkg", "-i", filename]) + else: +@@ -1399,7 +1396,7 @@ def handleSigchld(number, frame): + setSigchldHandler() + lng.Lang() + +-wx.Log_EnableLogging(False) ++wx.Log.EnableLogging(False) + + app = PlayOnLinuxApp(redirect=False) + app.MainLoop() +--- a/python/setupwindow/gui_server.py ++++ b/python/setupwindow/gui_server.py +@@ -1,5 +1,5 @@ +-#!/usr/bin/python +-# -*- coding:Utf-8 -*- ++#!/usr/bin/python3 ++# -*- coding:utf-8 -*- + + # Copyright (C) 2008 Pâris Quentin + # This program is free software; you can redistribute it and/or modify +@@ -20,12 +20,12 @@ import os + import random + import socket + import string +-import thread ++import _thread as thread + import threading + import time + import wx + +-from POL_SetupFrame import POL_SetupFrame ++from .POL_SetupFrame import POL_SetupFrame + + + class gui_server(threading.Thread): +@@ -37,20 +37,20 @@ class gui_server(threading.Thread): + # This dictionary will contain every created setup window + self.parent = parent + +- def GenCookie(self, length=20, chars=string.letters + string.digits): ++ def GenCookie(self, length=20, chars=string.ascii_letters + string.digits): + return ''.join([random.SystemRandom().choice(chars) for i in range(length)]) + + def handler(self, connection, addr): + self.temp = "" + while True: +- self.tempc = connection.recv(2048) ++ self.tempc = connection.recv(2048).decode() + + self.temp += self.tempc + if "\n" in self.tempc: + break + + self.result = self.interact(self.temp.replace("\n", "")) +- connection.send(self.result) ++ connection.send(self.result.encode()) + try: + connection.shutdown(1) + connection.close() +@@ -59,7 +59,7 @@ class gui_server(threading.Thread): + + def initServer(self): + if (self._port >= 30020): +- print _("Error: Unable to reserve a valid port") ++ print(_("Error: Unable to reserve a valid port")) + wx.MessageBox(_("Error: Unable to reserve a valid port"), os.environ["APPLICATION_TITLE"]) + os._exit(0) + +@@ -69,7 +69,7 @@ class gui_server(threading.Thread): + self.acceptor.listen(10) + os.environ["POL_PORT"] = str(self._port) + os.environ["POL_COOKIE"] = self.GenCookie() +- except socket.error, msg: ++ except socket.error as msg: + self._port += 1 + self.initServer() + +@@ -112,8 +112,8 @@ class gui_server(threading.Thread): + while self._running: + try: + self.connection, self.addr = self.acceptor.accept() +- except socket.error as (errno, msg): +- if errno == 4: # Interrupted system call ++ except socket.error as e: ++ if e.errno == 4: # Interrupted system call + continue + + thread.start_new_thread(self.handler, (self.connection, self.addr)) +@@ -122,7 +122,7 @@ class gui_server(threading.Thread): + ## FIXME: To be refactored + def readAction(object): + if (object.SetupWindowTimer_action[0] != os.environ["POL_COOKIE"]): +- print "Bad cookie!" ++ print("Bad cookie!") + object.SetupWindowTimer_action = None + return False + +--- a/python/wine_versions/WineVersionsFetcher.py ++++ b/python/wine_versions/WineVersionsFetcher.py +@@ -2,10 +2,10 @@ import json + import os + import threading + import time +-import urllib2 ++import urllib.request ++import natsort + + from lib import Variables +-from lib.playonlinux import keynat + from wine_versions.WineVersionsTools import fetchUserOS + + class WineVersionFetcher(threading.Thread): +@@ -28,8 +28,8 @@ class WineVersionFetcher(threading.Threa + wfolder = "-".join([fetchUserOS(), self.architecture]) + + +- req = urllib2.Request(url, None, {'User-Agent': Variables.userAgent}) +- handle = urllib2.urlopen(req, timeout = 5) ++ req = urllib.request.Request(url, None, {'User-Agent': Variables.userAgent}) ++ handle = urllib.request.urlopen(req, timeout = 5) + time.sleep(1) + available_distributions = json.loads(handle.read()) + self.versions_ = [] +@@ -53,11 +53,11 @@ class WineVersionFetcher(threading.Threa + else: + print(distribution["name"] + " does not match") + +- self.versions_.sort(key=keynat) ++ self.versions_.sort(key=natsort.natsort_keygen()) + self.versions_.reverse() + self.versions = self.versions_[:] + self.thread_message = "Ok" +- except Exception, e: ++ except Exception as e: + print(e) + time.sleep(1) + self.thread_message = "Err" +--- a/python/wrapper.py ++++ b/python/wrapper.py +@@ -1,4 +1,4 @@ +-#!/usr/bin/env python ++#!/usr/bin/python3 + # -*- coding: utf-8 -*- + + # Copyright (C) 2008 Pâris Quentin +@@ -25,13 +25,9 @@ import os, time, sys, subprocess, signal + try : + os.environ["POL_OS"] + except : +- print "ERROR ! Please define POL_OS environment var first." ++ print("ERROR ! Please define POL_OS environment var first.") + os._exit(1) + +-if(os.environ["POL_OS"] == "Linux"): +- import wxversion +- wxversion.ensureMinimal('2.8') +- + import wx + import lib.lng as lng + import lib.Variables as Variables +@@ -94,9 +90,13 @@ class MainWindow(wx.Frame): + return False + + def ForceClose(self, signal, frame): # Catch SIGINT +- print "\nCtrl+C pressed. Killing all processes..." ++ print("\nCtrl+C pressed. Killing all processes...") + self.POLDie() + ++ def Destroy(self): ++ self.SetupWindowTimer.Stop() ++ return super().Destroy() ++ + + class Program(threading.Thread): + def __init__(self): +@@ -107,7 +107,7 @@ class Program(threading.Thread): + def run(self): + self.running = True + self.chaine = "" +- print "Script started "+sys.argv[1] ++ print("Script started "+sys.argv[1]) + for arg in sys.argv[2:]: + self.chaine+=" \""+arg+"\"" + self.proc = subprocess.Popen("bash \""+sys.argv[1]+"\""+self.chaine, shell=True) +--- a/python/install/InstallWindow.py ++++ b/python/install/InstallWindow.py +@@ -1,4 +1,4 @@ +-#!/usr/bin/env python ++#!/usr/bin/python3 + # -*- coding: utf-8 -*- + + # Copyright (C) 2008 Pâris Quentin +@@ -19,13 +19,12 @@ + + import codecs + import os +-import string + import subprocess + + import wx +-import wx.animate ++import wx.adv + import wx.html +-import wx.lib.hyperlink ++import wx.lib.agw.hyperlink + from wx.lib.ClickableHtmlWindow import PyClickableHtmlWindow + + import lib.Variables as Variables +@@ -46,7 +45,7 @@ class InstallWindow(PlayOnLinuxWindow): + self.cats_icons[name] = wx.BitmapButton(self.installWindowHeader, 2000 + iid, wx.Bitmap(icon), (0, 0), + style=wx.NO_BORDER) + +- self.cats_links[name] = wx.lib.hyperlink.HyperLinkCtrl(self.installWindowHeader, 3000 + iid, name, pos=(0, 52)) ++ self.cats_links[name] = wx.lib.agw.hyperlink.HyperLinkCtrl(self.installWindowHeader, 3000 + iid, name, pos=(0, 52)) + mataille = self.cats_links[name].GetSize()[0] + + mataille2 = self.cats_icons[name].GetSize()[0] +@@ -55,8 +54,8 @@ class InstallWindow(PlayOnLinuxWindow): + self.cats_links[name].SetPosition((espace * iid + (espace - mataille / 1.3) / 2, 47)) + self.cats_icons[name].SetPosition((image_pos, offset)) + +- wx.lib.hyperlink.EVT_HYPERLINK_LEFT(self, 3000 + iid, self.AddApps) +- wx.EVT_BUTTON(self, 2000 + iid, self.AddApps) ++ self.Bind(wx.lib.agw.hyperlink.EVT_HYPERLINK_LEFT, self.AddApps, id=3000 + iid) ++ self.Bind(wx.EVT_BUTTON, self.AddApps, id=2000 + iid) + + self.cats_links[name].SetColours(wx.Colour(0, 0, 0), wx.Colour(0, 0, 0), wx.Colour(0, 0, 0)) + self.cats_links[name].AutoBrowse(False) +@@ -116,9 +115,9 @@ class InstallWindow(PlayOnLinuxWindow): + self.installWindowBodySizer.Add(self.panelWait, 1, wx.EXPAND) + self.panelWait.Hide() + ## FIXME: Remove those magic numbers +- self.animation_wait = wx.animate.GIFAnimationCtrl(self.panelWait, -1, +- Variables.playonlinux_env + "/resources/images/install/wait.gif", +- ((800 - 128) / 2, (550 - 128) / 2 - 71)) ++ self.animation_wait = wx.adv.AnimationCtrl(self.panelWait, -1, ++ pos=((800 - 128) / 2, (550 - 128) / 2 - 71)) ++ self.animation_wait.LoadFile(Variables.playonlinux_env + "/resources/images/install/wait.gif") + self.percentageText = wx.StaticText(self.panelWait, -1, "", ((800 - 30) / 2, (550 - 128) / 2 + 128 + 10 - 71), + wx.DefaultSize) + self.percentageText.SetFont(self.fontTitle) +@@ -202,7 +201,8 @@ class InstallWindow(PlayOnLinuxWindow): + + self.descriptionLoaderPanel = wx.Panel(appDescriptionPanel, -1, style=Variables.widget_borders) + self.descriptionLoaderPanel.SetBackgroundColour((255, 255, 255)) +- self.animation = wx.animate.GIFAnimationCtrl(self.descriptionLoaderPanel, -1, Variables.playonlinux_env + "/resources/images/install/wait_mini.gif", (90, 100)) ++ self.animation = wx.adv.AnimationCtrl(self.descriptionLoaderPanel, -1, pos=(90, 100)) ++ self.animation.LoadFile(Variables.playonlinux_env + "/resources/images/install/wait_mini.gif") + self.animation.Hide() + self.descriptionLoaderPanel.Hide() + +@@ -227,7 +227,7 @@ class InstallWindow(PlayOnLinuxWindow): + self.cancelButton = wx.Button(buttonsPanel, wx.ID_CLOSE, _("Cancel")) + self.installButton = wx.Button(buttonsPanel, wx.ID_APPLY, _("Install")) + self.updateButton = wx.Button(buttonsPanel, wx.ID_REFRESH, _("Refresh")) +- self.manualInstall = wx.lib.hyperlink.HyperLinkCtrl(buttonsPanel, 111, _("Install a non-listed program")) ++ self.manualInstall = wx.lib.agw.hyperlink.HyperLinkCtrl(buttonsPanel, 111, _("Install a non-listed program")) + self.manualInstall.SetColours(wx.Colour(0, 0, 0), wx.Colour(0, 0, 0), wx.Colour(0, 0, 0)) + self.manualInstall.AutoBrowse(False) + self.manualInstall.UpdateLink(True) +@@ -277,18 +277,18 @@ class InstallWindow(PlayOnLinuxWindow): + + + # wx.EVT_TREE_SEL_CHANGED(self, 105, self.AddApps) +- wx.EVT_TREE_SEL_CHANGED(self, 106, self.AppsDetails) +- wx.EVT_BUTTON(self, wx.ID_CLOSE, self.closeapp) +- wx.EVT_BUTTON(self, wx.ID_APPLY, self.installapp) +- wx.EVT_BUTTON(self, wx.ID_REFRESH, self.UpdatePol) +- wx.EVT_CLOSE(self, self.closeapp) +- wx.EVT_TREE_ITEM_ACTIVATED(self, 106, self.installapp) +- wx.EVT_TEXT(self, 110, self.search) +- wx.lib.hyperlink.EVT_HYPERLINK_LEFT(self, 111, self.manual) +- +- wx.EVT_CHECKBOX(self, 401, self.CheckBoxReload) +- wx.EVT_CHECKBOX(self, 402, self.CheckBoxReload) +- wx.EVT_CHECKBOX(self, 403, self.CheckBoxReload) ++ self.Bind(wx.EVT_TREE_SEL_CHANGED, self.AppsDetails, id=106) ++ self.Bind(wx.EVT_BUTTON, self.closeapp, id=wx.ID_CLOSE) ++ self.Bind(wx.EVT_BUTTON, self.installapp, id=wx.ID_APPLY) ++ self.Bind(wx.EVT_BUTTON, self.UpdatePol, id=wx.ID_REFRESH) ++ self.Bind(wx.EVT_CLOSE, self.closeapp) ++ self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.installapp, id=106) ++ self.Bind(wx.EVT_TEXT, self.search, id=110) ++ self.Bind(wx.lib.agw.hyperlink.EVT_HYPERLINK_LEFT, self.manual, id=111) ++ ++ self.Bind(wx.EVT_CHECKBOX, self.CheckBoxReload, id=401) ++ self.Bind(wx.EVT_CHECKBOX, self.CheckBoxReload, id=402) ++ self.Bind(wx.EVT_CHECKBOX, self.CheckBoxReload, id=403) + + def TimerAction(self, event): + try: +@@ -339,7 +339,7 @@ class InstallWindow(PlayOnLinuxWindow): + starWidth = 20 + self.panelStars.DestroyChildren() + +- for i in xrange(int(stars)): ++ for i in range(int(stars)): + wx.StaticBitmap(self.panelStars, -1, + wx.Bitmap(Variables.playonlinux_env + "/etc/star.png"), + (i * starWidth, 0), wx.DefaultSize) +@@ -378,7 +378,7 @@ class InstallWindow(PlayOnLinuxWindow): + _("Please read this")) + + subprocess.Popen( +- ["bash", Variables.playonlinux_env + "/bash/install", InstallApplication.encode("utf-8", "replace")]) ++ ["bash", Variables.playonlinux_env + "/bash/install", InstallApplication]) + + self.Destroy() + return +@@ -397,14 +397,14 @@ class InstallWindow(PlayOnLinuxWindow): + self.search_result = [] + + while (self.j < len(self.apps)): +- if (string.lower(self.user_search) in string.lower(self.apps[self.j])): ++ if (self.user_search.lower() in self.apps[self.j].lower()): + self.search_result.append(self.apps[self.j]) + self.k = self.k + 1 + self.j = self.j + 1 + + if (len(self.user_search) < 2 or "~" in self.user_search): + self.search_result = [] +- self.user_search_cut = string.split(self.user_search, ":") ++ self.user_search_cut = self.user_search.split(":") + if (len(self.user_search_cut) > 1): + if (self.user_search_cut[0] == "get" and self.user_search_cut[1].isdigit()): + self.search_result.append(self.user_search) +@@ -431,7 +431,7 @@ class InstallWindow(PlayOnLinuxWindow): + self.DelApps() + self.root_apps = self.appsList.AddRoot("") + self.i = 0 +- array.sort(key=unicode.upper) ++ array.sort(key=str.upper) + for app in array: + app_array = app.split("~") + appname = app_array[0] +@@ -454,7 +454,7 @@ class InstallWindow(PlayOnLinuxWindow): + + if (show == True): + self.icon_look_for = Variables.playonlinux_rep + "/configurations/icones/" + appname +- if (os.path.exists(self.icon_look_for.encode('utf-8', 'ignore'))): ++ if (os.path.exists(self.icon_look_for)): + try: + bitmap = wx.Image(self.icon_look_for) + bitmap.Rescale(22, 22, wx.IMAGE_QUALITY_HIGH) +@@ -464,7 +464,7 @@ class InstallWindow(PlayOnLinuxWindow): + pass + else: + self.imagesapps.Add(wx.Bitmap(Variables.playonlinux_env + "/etc/playonlinux22.png")) +- itemId = self.appsList.AppendItem(self.root_apps, appname.encode('utf-8', 'ignore'), self.i) ++ itemId = self.appsList.AppendItem(self.root_apps, appname, self.i) + if testing == 1: + # (255,255,214) is web site color for beta, but it's not very visible next to plain white, + # and red is the color of danger +@@ -541,3 +541,7 @@ class InstallWindow(PlayOnLinuxWindow): + self.apps[self.j] = self.apps[self.j].replace("\n", "") + self.j += 1 + self.WriteApps(self.apps) ++ ++ def Destroy(self): ++ self.timer.Stop() ++ super().Destroy() +--- a/python/lib/wine.py ++++ b/python/lib/wine.py +@@ -1,9 +1,10 @@ +-#!/usr/bin/env python ++#!/usr/bin/python3 + # -*- coding: utf-8 -*- + + # Copyright (C) 2007-2010 PlayOnLinux Team + +-import Variables, os, string ++from . import Variables ++import os + + + def LoadRegValues(prefix, values): +@@ -25,7 +26,7 @@ def LoadRegValues(prefix, values): + for element in values: + if(element in line): + line = line.replace("\"","").rstrip("\\0") # workaround for Wine bug #37575 +- line = string.split(line, "=") ++ line = line.split("=") + line = line[1] + result[element] = line + found = True +--- a/python/options.py ++++ b/python/options.py +@@ -1,4 +1,4 @@ +-#!/usr/bin/env python ++#!/usr/bin/python3 + # -*- coding: utf-8 -*- + + # Copyright (C) 2009 Pâris Quentin +@@ -19,7 +19,7 @@ + # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + from asyncore import dispatcher +-import wxversion, os, subprocess, getopt, sys, urllib, signal, socket, string ++import os, subprocess, getopt, sys, urllib.request, signal, socket + import wx, time, re + import webbrowser, shutil + import threading, time, codecs +@@ -45,15 +45,15 @@ class getPlugins(threading.Thread): + if(self.thread_message == "get"): + try : + url = 'http://mulx.playonlinux.com/wine/linux-i386/LIST' +- req = urllib2.Request(url) +- handle = urllib2.urlopen(req) ++ req = urllib.request.Request(url) ++ handle = urllib.request.urlopen(req) + time.sleep(1) + available_versions = handle.read() +- available_versions = string.split(available_versions,"\n") ++ available_versions = available_versions.split("\n") + self.i = 0 + self.versions_ = [] + while(self.i < len(available_versions) - 1): +- informations = string.split(available_versions[self.i], ";") ++ informations = available_versions[self.i].split(";") + version = informations[1] + package = informations[0] + sha1sum = informations[2] +@@ -140,7 +140,7 @@ class Onglets(wx.Notebook): + self.ProxyTxtPass = wx.StaticText(self.panelInternet, -1, _("Proxy password"), (10,240), wx.DefaultSize) + self.ProxyPass = wx.TextCtrl(self.panelInternet, -1, proxy_settings["PROXY_PASS"], pos=(20,260),size=(300,27), style=wx.TE_PASSWORD) + self.AddPage(self.panelInternet, nom, imageId=2) +- wx.EVT_CHECKBOX(self, 120, self.proxy_enable) ++ self.Bind(wx.EVT_CHECKBOX, self.proxy_enable, id=120) + self.proxy_enable(self) + + def proxy_enable(self, event): +@@ -212,13 +212,13 @@ class Onglets(wx.Notebook): + + self.AddPage(self.panelPlugins, nom, imageId=5) + +- wx.EVT_TREE_SEL_CHANGED(self, 220, self.choose_plugin) ++ self.Bind(wx.EVT_TREE_SEL_CHANGED, self.choose_plugin, id=220) + +- wx.EVT_BUTTON(self, 214, self.disable) +- wx.EVT_BUTTON(self, 213, self.enable) +- wx.EVT_BUTTON(self, 212, self.setup_plug) +- wx.EVT_BUTTON(self, wx.ID_REMOVE, self.delete_plug) +- wx.EVT_BUTTON(self, wx.ID_ADD, self.add_plug) ++ self.Bind(wx.EVT_BUTTON, self.disable, id=214) ++ self.Bind(wx.EVT_BUTTON, self.enable, id=213) ++ self.Bind(wx.EVT_BUTTON, self.setup_plug, id=212) ++ self.Bind(wx.EVT_BUTTON, self.delete_plug, id=wx.ID_REMOVE) ++ self.Bind(wx.EVT_BUTTON, self.add_plug, id=wx.ID_ADD) + + def generateExts(self): + self.list_ext.DeleteAllItems() +@@ -227,7 +227,7 @@ class Onglets(wx.Notebook): + self.exts.sort() + for line in self.exts: + line = line.replace("\n","") +- line = string.split(line,"=") ++ line = line.split("=") + liner = "Line %s" % i + self.list_ext.InsertStringItem(i, liner) + self.list_ext.SetStringItem(i, 0, line[0]) +@@ -249,8 +249,8 @@ class Onglets(wx.Notebook): + self.app_installed.Show() + self.delete_ext.Show() + +- self.app_selected = string.split(self.exts[event.m_itemIndex],"=")[1] +- self.ext_selected = string.split(self.exts[event.m_itemIndex],"=")[0] ++ self.app_selected = self.exts[event.m_itemIndex].split("=")[1] ++ self.ext_selected = self.exts[event.m_itemIndex].split("=")[0] + + self.app_installed.SetValue(self.app_selected.replace("\n","").replace("\r","")) + +@@ -285,10 +285,10 @@ class Onglets(wx.Notebook): + + self.generateExts() + self.AddPage(self.panelExt, nom, imageId=6) +- wx.EVT_LIST_ITEM_SELECTED(self, 500, self.editExt) +- wx.EVT_COMBOBOX(self, 501, self.reditExt) +- wx.EVT_BUTTON(self, 502, self.delExt) +- wx.EVT_BUTTON(self, 503, self.newExt) ++ self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.editExt, id=500) ++ self.Bind(wx.EVT_COMBOBOX, self.reditExt, id=501) ++ self.Bind(wx.EVT_BUTTON, self.delExt, id=502) ++ self.Bind(wx.EVT_BUTTON, self.newExt, id=503) + + def setup_plug(self, event): + self.current_plugin = self.pluginlist.GetItemText(self.pluginlist.GetSelection()) +@@ -301,15 +301,15 @@ class Onglets(wx.Notebook): + self.FileDialog.SetWildcard("POL Packages (*.pol)|*.pol") + result = self.FileDialog.ShowModal() + if(result == wx.ID_OK and self.FileDialog.GetPath() != ""): +- if(wx.YES == wx.MessageBox(_("Are you sure you want to install: ").decode("utf-8","replace")+self.FileDialog.GetPath()+"?",os.environ["APPLICATION_TITLE"] ,style=wx.YES_NO | wx.ICON_QUESTION)): +- subprocess.call(["bash", Variables.playonlinux_env+"/playonlinux-pkg", "-i", self.FileDialog.GetPath().encode("utf-8","replace")]) ++ if(wx.YES == wx.MessageBox(_("Are you sure you want to install: ")+self.FileDialog.GetPath()+"?",os.environ["APPLICATION_TITLE"] ,style=wx.YES_NO | wx.ICON_QUESTION)): ++ subprocess.call(["bash", Variables.playonlinux_env+"/playonlinux-pkg", "-i", self.FileDialog.GetPath()]) + self.LoadPlugins() + self.FileDialog.Destroy() + + def delete_plug(self, event): + self.current_plugin = self.pluginlist.GetItemText(self.pluginlist.GetSelection()) + self.plugin_path = Variables.playonlinux_rep+"/plugins/"+self.current_plugin +- if(wx.YES == wx.MessageBox(_("Are you sure you want to delete: ").decode("utf-8","replace")+self.current_plugin+"?", os.environ["APPLICATION_TITLE"],style=wx.YES_NO | wx.ICON_QUESTION)): ++ if(wx.YES == wx.MessageBox(_("Are you sure you want to delete: ")+self.current_plugin+"?", os.environ["APPLICATION_TITLE"],style=wx.YES_NO | wx.ICON_QUESTION)): + shutil.rmtree(self.plugin_path) + self.LoadPlugins() + def disable(self, event): +@@ -367,7 +367,7 @@ class Onglets(wx.Notebook): + + class MainWindow(wx.Frame): + def __init__(self,parent,id,title,onglet): +- wx.Frame.__init__(self, parent, -1, title, size = (505, 550), style = wx.CLOSE_BOX | wx.CAPTION | wx.MINIMIZE_BOX) ++ wx.Frame.__init__(self, parent, -1, title, size = (505, 550), style = wx.CLOSE_BOX | wx.CAPTION | wx.MINIMIZE_BOX | wx.RESIZE_BORDER) + self.SetIcon(wx.Icon(Variables.playonlinux_env+"/etc/playonlinux.png", wx.BITMAP_TYPE_ANY)) + self.panelFenp = wx.Panel(self, -1) + self.panels_buttons = wx.Panel(self.panelFenp, -1) +@@ -393,8 +393,8 @@ class MainWindow(wx.Frame): + + self.panelFenp.SetSizer(self.sizer) + self.panelFenp.SetAutoLayout(True) +- wx.EVT_BUTTON(self, wx.ID_APPLY, self.apply_settings) +- wx.EVT_BUTTON(self, wx.ID_CLOSE, self.app_Close) ++ self.Bind(wx.EVT_BUTTON, self.apply_settings, id=wx.ID_APPLY) ++ self.Bind(wx.EVT_BUTTON, self.app_Close, id=wx.ID_CLOSE) + + def app_Close(self, event): + self.Destroy() +--- a/python/wine_versions/WineVersionsWindow.py ++++ b/python/wine_versions/WineVersionsWindow.py +@@ -1,4 +1,4 @@ +-#!/usr/bin/env python ++#!/usr/bin/python3 + # -*- coding: utf-8 -*- + + # Copyright (C) 2007 Pâris Quentin +@@ -23,7 +23,7 @@ import shutil + import subprocess + + import wx +-import wx.animate ++import natsort + + import lib.Variables as Variables + import lib.lng +@@ -104,17 +104,17 @@ class WineVersionsWindow(wx.Frame): + + # self.button = wx.Button(self.panels_buttons, wx.ID_CLOSE, _("Close"), pos=(510, 5), size=wx.DefaultSize) + +- wx.EVT_BUTTON(self, wx.ID_CLOSE, self.closeapp) +- wx.EVT_CLOSE(self, self.closeapp) +- wx.EVT_TREE_SEL_CHANGED(self, 106, self.unselect32) +- wx.EVT_TREE_SEL_CHANGED(self, 107, self.unselect32) +- wx.EVT_BUTTON(self, 108, self.delete32) +- wx.EVT_BUTTON(self, 109, self.install32) +- +- wx.EVT_TREE_SEL_CHANGED(self, 206, self.unselect64) +- wx.EVT_TREE_SEL_CHANGED(self, 207, self.unselect64) +- wx.EVT_BUTTON(self, 208, self.delete64) +- wx.EVT_BUTTON(self, 209, self.install64) ++ self.Bind(wx.EVT_BUTTON, self.closeapp, id=wx.ID_CLOSE) ++ self.Bind(wx.EVT_CLOSE, self.closeapp) ++ self.Bind(wx.EVT_TREE_SEL_CHANGED, self.unselect32, id=106) ++ self.Bind(wx.EVT_TREE_SEL_CHANGED, self.unselect32, id=107) ++ self.Bind(wx.EVT_BUTTON, self.delete32, id=108) ++ self.Bind(wx.EVT_BUTTON, self.install32, id=109) ++ ++ self.Bind(wx.EVT_TREE_SEL_CHANGED, self.unselect64, id=206) ++ self.Bind(wx.EVT_TREE_SEL_CHANGED, self.unselect64, id=207) ++ self.Bind(wx.EVT_BUTTON, self.delete64, id=208) ++ self.Bind(wx.EVT_BUTTON, self.install64, id=209) + + self.Bind(wx.EVT_TIMER, self.AutoReload, self.timer) + self.timer.Start(200) +@@ -170,12 +170,12 @@ class WineVersionsWindow(wx.Frame): + + def delete_common(self, event, arch): + version = self.onglets.installedWineVersionsTreeSelector[arch].GetItemText( +- self.onglets.installedWineVersionsTreeSelector[arch].GetSelection()).encode("utf-8", "replace") ++ self.onglets.installedWineVersionsTreeSelector[arch].GetSelection()) + used_version = self.checkVersionUse(arch) # Get the set of wine version used by wineprefix + message = _('Are you sure you want to delete wine {0}?').format(version) + if version in used_version: + message += "\n" + _('This version is CURRENTLY IN USE') +- if (wx.YES == wx.MessageBox(message.decode("utf-8", "replace"), os.environ["APPLICATION_TITLE"], ++ if (wx.YES == wx.MessageBox(message, os.environ["APPLICATION_TITLE"], + style=wx.YES_NO | wx.ICON_QUESTION)): + shutil.rmtree(Variables.playonlinux_rep + "/wine/" + fetchUserOS() + "-" + arch + "/" + version) + +@@ -183,8 +183,7 @@ class WineVersionsWindow(wx.Frame): + self.install_common(event, "x86") + + def install_common(self, event, arch): +- install = self.onglets.availableWineVersionsTreeSelector[arch].GetItemText(self.onglets.availableWineVersionsTreeSelector[arch].GetSelection()).encode("utf-8", +- "replace") ++ install = self.onglets.availableWineVersionsTreeSelector[arch].GetItemText(self.onglets.availableWineVersionsTreeSelector[arch].GetSelection()) + subprocess.Popen(["bash", Variables.playonlinux_env + "/bash/install_wver", install, arch]) + + def unselect64(self, event): +@@ -246,7 +245,7 @@ class WineVersionsWindow(wx.Frame): + used_version = self.checkVersionUse(arch) # Get the set of wine version used by wineprefix + + installed_versions = os.listdir(Variables.playonlinux_rep + "/wine/" + wfolder) +- installed_versions.sort(key=playonlinux.keynat) ++ installed_versions.sort(key=natsort.natsort_keygen()) + installed_versions.reverse() + self.i = 0 + self.j = 0 +@@ -282,3 +281,7 @@ class WineVersionsWindow(wx.Frame): + self.download64.thread_running = False + + self.Destroy() ++ ++ def Destroy(self): ++ self.timer.Stop() ++ return super().Destroy() +--- a/python/lib/__init__.py ++++ b/python/lib/__init__.py +@@ -1 +1 @@ +-#!/usr/bin/python ++#!/usr/bin/python3 +--- a/python/lib/lng.py ++++ b/python/lib/lng.py +@@ -1,10 +1,10 @@ +-#!/usr/bin/python ++#!/usr/bin/python3 + # -*- coding: utf-8 -*- + + import wx +-import wxversion +-import gettext, Variables as Variables, os +-import locale, string ++import gettext, os ++from . import Variables ++import locale + + class Lang(object): + def __init__(self): +--- a/python/setupwindow/POL_SetupFrame.py ++++ b/python/setupwindow/POL_SetupFrame.py +@@ -1,4 +1,4 @@ +-#!/usr/bin/python ++#!/usr/bin/python3 + # -*- coding:utf-8 -*- + + # Copyright (C) 2008 Pâris Quentin +@@ -22,19 +22,18 @@ import signal + import subprocess + + import os +-import string + import time +-import urllib +-import urlparse ++import urllib.request ++import urllib.parse + import wx +-import wx.animate ++import wx.adv + + import lib.Variables as Variables + import lib.lng + import lib.playonlinux as playonlinux + + lib.lng.Lang() +-urllib.URLopener.version = Variables.userAgent # Arg ... ++urllib.request.URLopener.version = Variables.userAgent # Arg ... + + from ui.PlayOnLinuxWindow import PlayOnLinuxWindow + from setupwindow.Downloader import Downloader +@@ -71,7 +70,7 @@ class POL_SetupFrame(PlayOnLinuxWindow): + + self._createUI() + +- wx.EVT_CLOSE(self, self.Cancel) ++ self.Bind(wx.EVT_CLOSE, self.Cancel) + + def _createHeader(self): + self.header = wx.Panel(self, -1, size=(522, 65)) +@@ -102,7 +101,7 @@ class POL_SetupFrame(PlayOnLinuxWindow): + self.InfoScript.Hide() + self.script_ID = 0 + self.InfoScript.Bind(wx.EVT_LEFT_DOWN, self.InfoClick) +- self.InfoScript.SetCursor(wx.StockCursor(wx.CURSOR_HAND)) ++ self.InfoScript.SetCursor(wx.Cursor(wx.CURSOR_HAND)) + self.footerSizer.Add(self.InfoScript, 0, wx.EXPAND) + + self.DebugScript = wx.StaticBitmap(self.footer, -1, +@@ -110,7 +109,7 @@ class POL_SetupFrame(PlayOnLinuxWindow): + self.DebugScript.Hide() + self.script_LOGTITLE = None + self.DebugScript.Bind(wx.EVT_LEFT_DOWN, self.DebugClick) +- self.DebugScript.SetCursor(wx.StockCursor(wx.CURSOR_HAND)) ++ self.DebugScript.SetCursor(wx.Cursor(wx.CURSOR_HAND)) + self.footerSizer.Add(self.DebugScript, 0, wx.EXPAND) + + +@@ -220,17 +219,17 @@ class POL_SetupFrame(PlayOnLinuxWindow): + self.password = wx.StaticText(self.contentPanel, -1, _("Password: "), pos=(20, 70), size=(460, 20)) + self.loginbox = wx.TextCtrl(self.contentPanel, -1, "", size=(250, 22), pos=(200, 35)) + self.passbox = wx.TextCtrl(self.contentPanel, -1, "", size=(250, 22), pos=(200, 65), style=wx.TE_PASSWORD) +- self.register = wx.HyperlinkCtrl(self.contentPanel, 303, _("Register"), "", pos=(20, 100)) ++ self.register = wx.adv.HyperlinkCtrl(self.contentPanel, 303, _("Register"), "", pos=(20, 100)) + self.register.SetNormalColour(wx.Colour(0, 0, 0)) + + # Fixed Events +- wx.EVT_BUTTON(self, wx.ID_YES, self.release_yes) +- wx.EVT_BUTTON(self, wx.ID_NO, self.release_no) +- wx.EVT_BUTTON(self, wx.ID_CANCEL, self.Cancel) +- wx.EVT_BUTTON(self, 103, self.Parcourir) +- wx.EVT_CHECKBOX(self, 302, self.agree) +- wx.EVT_CHECKBOX(self, 304, self.switch_menu) +- wx.EVT_HYPERLINK(self, 303, self.POL_register) ++ self.Bind(wx.EVT_BUTTON, self.release_yes, id=wx.ID_YES) ++ self.Bind(wx.EVT_BUTTON, self.release_no, id=wx.ID_NO) ++ self.Bind(wx.EVT_BUTTON, self.Cancel, id=wx.ID_CANCEL) ++ self.Bind(wx.EVT_BUTTON, self.Parcourir, id=103) ++ self.Bind(wx.EVT_CHECKBOX, self.agree, id=302) ++ self.Bind(wx.EVT_CHECKBOX, self.switch_menu, id=304) ++ self.Bind(wx.adv.EVT_HYPERLINK, self.POL_register, id=303) + + # Debug Window + self.debugImage = wx.StaticBitmap(self.contentPanel, -1, wx.Bitmap( +@@ -350,23 +349,23 @@ class POL_SetupFrame(PlayOnLinuxWindow): + + self.DrawCancel() + self.DrawNext() +- wx.EVT_BUTTON(self, wx.ID_FORWARD, self.release) ++ self.Bind(wx.EVT_BUTTON, self.release, id=wx.ID_FORWARD) + + def POL_SetupWindow_free_presentation(self, message, titre): + self.resetSetupWindow() + self.contentPanel.Hide() + self.presentationPanel.Show() +- self.titreP.SetLabel(titre.decode("utf8", "replace")) ++ self.titreP.SetLabel(titre) + self.titreP.Wrap(280) + +- self.texteP.SetLabel(message.decode("utf8", "replace").replace("\\n", "\n").replace("\\t", "\t")) ++ self.texteP.SetLabel(message.replace("\\n", "\n").replace("\\t", "\t")) + self.texteP.Wrap(360) + self.texteP.Show() + + self.DrawCancel() + self.DrawNext() + +- wx.EVT_BUTTON(self, wx.ID_FORWARD, self.release) ++ self.Bind(wx.EVT_BUTTON, self.release, id=wx.ID_FORWARD) + self.DrawImage() + self.Layout() + +@@ -410,8 +409,8 @@ class POL_SetupFrame(PlayOnLinuxWindow): + + self.DrawCancel() + self.DrawNext() +- wx.EVT_BUTTON(self, wx.ID_FORWARD, self.release_champ) +- wx.EVT_TEXT_ENTER(self, 400, self.release_champ) ++ self.Bind(wx.EVT_BUTTON, self.release_champ, id=wx.ID_FORWARD) ++ self.Bind(wx.EVT_TEXT_ENTER, self.release_champ, id=400) + self.Layout() + + def POL_Debug(self, message, title, value): +@@ -498,7 +497,7 @@ class POL_SetupFrame(PlayOnLinuxWindow): + self.DrawDefault(message, title) + + self.space = message.count("\\n") + 1 +- self.areaList = string.split(liste, cut) ++ self.areaList = liste.split(cut) + + self.Menu.SetPosition((20, 5 + self.space * 16)) + +@@ -511,11 +510,11 @@ class POL_SetupFrame(PlayOnLinuxWindow): + self.DrawNext() + + if (numtype == False): +- wx.EVT_BUTTON(self, wx.ID_FORWARD, self.release_menu) +- wx.EVT_LISTBOX_DCLICK(self, 104, self.release_menu) ++ self.Bind(wx.EVT_BUTTON, self.release_menu, id=wx.ID_FORWARD) ++ self.Bind(wx.EVT_LISTBOX_DCLICK, self.release_menu, id=104) + else: +- wx.EVT_BUTTON(self, wx.ID_FORWARD, self.release_menu_num) +- wx.EVT_LISTBOX_DCLICK(self, 104, self.release_menu_num) ++ self.Bind(wx.EVT_BUTTON, self.release_menu_num, id=wx.ID_FORWARD) ++ self.Bind(wx.EVT_LISTBOX_DCLICK, self.release_menu_num, id=104) + self.Layout() + + def POL_SetupWindow_browse(self, message, title, value, directory, supportedfiles): +@@ -544,7 +543,7 @@ class POL_SetupFrame(PlayOnLinuxWindow): + self.DrawCancel() + self.DrawNext() + +- wx.EVT_BUTTON(self, wx.ID_FORWARD, self.release_login) ++ self.Bind(wx.EVT_BUTTON, self.release_login, id=wx.ID_FORWARD) + self.Layout() + + def POL_SetupWindow_textbox_multiline(self, message, title, value): +@@ -559,7 +558,7 @@ class POL_SetupFrame(PlayOnLinuxWindow): + + self.DrawCancel() + self.DrawNext() +- wx.EVT_BUTTON(self, wx.ID_FORWARD, self.release_bigchamp) ++ self.Bind(wx.EVT_BUTTON, self.release_bigchamp, id=wx.ID_FORWARD) + self.Layout() + + def POL_SetupWindow_checkbox_list(self, message, title, liste, cut): +@@ -570,7 +569,7 @@ class POL_SetupFrame(PlayOnLinuxWindow): + self.space = message.count("\\n") + 1 + + self.scrolled_panel.SetPosition((20, 5 + self.space * 16)) +- self.areaList = string.split(liste, cut) ++ self.areaList = liste.split(cut) + + # We have to destroy all previous items (catching exception in case one is already destroyed) + self.i = 0 +@@ -594,7 +593,7 @@ class POL_SetupFrame(PlayOnLinuxWindow): + self.DrawCancel() + self.DrawNext() + self.separator = cut +- wx.EVT_BUTTON(self, wx.ID_FORWARD, self.release_checkboxes) ++ self.Bind(wx.EVT_BUTTON, self.release_checkboxes, id=wx.ID_FORWARD) + self.Layout() + + def POL_SetupWindow_shortcut_list(self, message, title): +@@ -609,8 +608,8 @@ class POL_SetupFrame(PlayOnLinuxWindow): + + self.DrawCancel() + self.DrawNext() +- wx.EVT_BUTTON(self, wx.ID_FORWARD, self.release_menugame) +- wx.EVT_TREE_ITEM_ACTIVATED(self, 111, self.release_menugame) ++ self.Bind(wx.EVT_BUTTON, self.release_menugame, id=wx.ID_FORWARD) ++ self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.release_menugame, id=111) + self.Layout() + + def POL_SetupWindow_icon_menu(self, message, title, items, cut, icon_folder, icon_list): +@@ -625,8 +624,8 @@ class POL_SetupFrame(PlayOnLinuxWindow): + + self.DrawCancel() + self.DrawNext() +- wx.EVT_BUTTON(self, wx.ID_FORWARD, self.release_menugame) +- wx.EVT_TREE_ITEM_ACTIVATED(self, 111, self.release_menugame) ++ self.Bind(wx.EVT_BUTTON, self.release_menugame, id=wx.ID_FORWARD) ++ self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.release_menugame, id=111) + self.Layout() + + def POL_SetupWindow_prefix_selector(self, message, title): +@@ -654,9 +653,9 @@ class POL_SetupFrame(PlayOnLinuxWindow): + self.DrawCancel() + self.DrawNext() + +- wx.EVT_BUTTON(self, wx.ID_FORWARD, self.release_menuprefixes) +- wx.EVT_TREE_ITEM_ACTIVATED(self, 111, self.release_menuprefixes) +- wx.EVT_LISTBOX_DCLICK(self, 104, self.release_menuprefixes) ++ self.Bind(wx.EVT_BUTTON, self.release_menuprefixes, id=wx.ID_FORWARD) ++ self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.release_menuprefixes, id=111) ++ self.Bind(wx.EVT_LISTBOX_DCLICK, self.release_menuprefixes, id=104) + + self.PCheckBox.Show() + self.Layout() +@@ -670,7 +669,7 @@ class POL_SetupFrame(PlayOnLinuxWindow): + + self.DrawCancel() + self.DrawNext() +- wx.EVT_BUTTON(self, wx.ID_FORWARD, self.release_notice) ++ self.Bind(wx.EVT_BUTTON, self.release_notice, id=wx.ID_FORWARD) + self.Layout() + + def POL_SetupWindow_licence(self, message, title, licence_file): +@@ -694,7 +693,7 @@ class POL_SetupFrame(PlayOnLinuxWindow): + self.DrawCancel() + self.DrawNext() + self.NextButton.Enable(False) +- wx.EVT_BUTTON(self, wx.ID_FORWARD, self.release) ++ self.Bind(wx.EVT_BUTTON, self.release, id=wx.ID_FORWARD) + self.Layout() + + def POL_SetupWindow_file(self, message, title, filetoread): +@@ -715,7 +714,7 @@ class POL_SetupFrame(PlayOnLinuxWindow): + + self.DrawCancel() + self.DrawNext() +- wx.EVT_BUTTON(self, wx.ID_FORWARD, self.release) ++ self.Bind(wx.EVT_BUTTON, self.release, id=wx.ID_FORWARD) + self.Layout() + + def POL_register(self, event): +@@ -726,7 +725,7 @@ class POL_SetupFrame(PlayOnLinuxWindow): + self.Layout() + + def RunCommand(self, event, command, confirm): +- if (confirm == "0" or wx.YES == wx.MessageBox(confirm.decode("utf-8", "replace"), ++ if (confirm == "0" or wx.YES == wx.MessageBox(confirm, + os.environ["APPLICATION_TITLE"], + style=wx.YES_NO | wx.ICON_QUESTION)): + subprocess.Popen(shlex.split(command)) +@@ -777,7 +776,7 @@ class POL_SetupFrame(PlayOnLinuxWindow): + if (self.item_check[i].IsChecked() == True): + send.append(self.areaList[i]) + i += 1 +- self.SendBash(string.join(send, self.separator)) ++ self.SendBash(self.separator.join(send)) + self.NextButton.Enable(False) + + def release_yes(self, event): +@@ -796,16 +795,15 @@ class POL_SetupFrame(PlayOnLinuxWindow): + + def release_login(self, event): + self.SendBash( +- self.loginbox.GetValue().encode("utf-8", "replace") + "~" + self.passbox.GetValue().encode("utf-8", +- "replace")) ++ self.loginbox.GetValue() + "~" + self.passbox.GetValue()) + self.NextButton.Enable(False) + + def release_champ(self, event): +- self.SendBash(self.champ.GetValue().encode("utf-8", "replace")) ++ self.SendBash(self.champ.GetValue()) + self.NextButton.Enable(False) + + def release_bigchamp(self, event): +- self.SendBashT(self.bigchamp.GetValue().replace("\n", "\\n").encode("utf-8", "replace")) ++ self.SendBashT(self.bigchamp.GetValue().replace("\n", "\\n")) + self.NextButton.Enable(False) + + def release_menu(self, event): +@@ -828,12 +826,12 @@ class POL_SetupFrame(PlayOnLinuxWindow): + self.NextButton.Enable(False) + + def release_menugame(self, event): +- self.SendBash(self.MenuGames.GetItemText(self.MenuGames.GetSelection()).encode("utf-8", "replace")) ++ self.SendBash(self.MenuGames.GetItemText(self.MenuGames.GetSelection())) + self.NextButton.Enable(False) + + def release_menuprefixes(self, event): + if (self.PCheckBox.IsChecked() == False): # Alors il faut renvoyer le prefix +- self.SendBash("1~" + self.MenuGames.GetItemText(self.MenuGames.GetSelection()).encode("utf-8", "replace")) ++ self.SendBash("1~" + self.MenuGames.GetItemText(self.MenuGames.GetSelection())) + else: + self.SendBash("2~" + self.areaList[self.Menu.GetSelection()]) + +@@ -917,7 +915,7 @@ class POL_SetupFrame(PlayOnLinuxWindow): + self.FileDialog.SetDirectory(self.directory) + self.FileDialog.ShowModal() + if (self.FileDialog.GetPath() != ""): +- filePath = self.FileDialog.GetPath().encode("utf-8", "replace") ++ filePath = self.FileDialog.GetPath() + filePathBaseName = filePath.split("/")[filePath.count("/")] + self.champ.SetValue(filePath) + self.NextButton.Enable(True) +@@ -958,7 +956,7 @@ class POL_SetupFrame(PlayOnLinuxWindow): + # * in a perfect world this should be removed and the + # client be always responsible for providing the filename + # it wants/expects +- self.chemin = urlparse.urlsplit(url)[2] ++ self.chemin = urllib.parse.urlsplit(url)[2] + self.nomFichier = self.chemin.split('/')[-1] + self.local = localB + self.nomFichier + else: +@@ -980,3 +978,7 @@ class POL_SetupFrame(PlayOnLinuxWindow): + self.MenuGames.Show() + self.Menu.Hide() + self.Refresh() ++ ++ def Destroy(self): ++ self.timer.Stop() ++ return super().Destroy() +--- a/tests/python/test_versionlower.py ++++ b/tests/python/test_versionlower.py +@@ -1,11 +1,10 @@ +-#!/usr/bin/python ++#!/usr/bin/python3 + import unittest + #import lib.playonlinux as playonlinux +-import string + + def VersionLower(version1, version2): +- version1 = string.split(version1, "-") +- version2 = string.split(version2, "-") ++ version1 = version1.split("-") ++ version2 = version2.split("-") + + try: + if(version1[1] != ""): +@@ -25,8 +24,8 @@ def VersionLower(version1, version2): + else: + return False + +- version1 = [ int(digit) for digit in string.split(version1[0],".") ] +- version2 = [ int(digit) for digit in string.split(version2[0],".") ] ++ version1 = [ int(digit) for digit in version1[0].split(".") ] ++ version2 = [ int(digit) for digit in version2[0].split(".") ] + + if(version1[0] < version2[0]): + return True +--- a/bash/find_python ++++ b/bash/find_python +@@ -13,6 +13,7 @@ search_python () { + POL_PYTHON="" + while true; do + # list of interpreter names to try, in order ++ next_python "python3" + next_python "python" + next_python "python2.7" + next_python "python2.6" +@@ -29,7 +30,7 @@ search_python () { + local Version=$($POL_PYTHON --version 2>&1 |tail -n 1|sed -e 's/^Python //') + echo -n "$Version - " 1>&2 + case "$Version" in +- 2.6|2.6.*|2.7|2.7.*) ++ 2.6|2.6.*|2.7|2.7.*|3.*) + if test_python; then + echo "selected" 1>&2 + return 0 +@@ -40,10 +41,6 @@ search_python () { + # Compatibility broken a while ago + echo "skipped" 1>&2 + ;; +- 3.*) +- # Not supported because of wxPython +- echo "skipped" 1>&2 +- ;; + *) + echo "unexpected version" 1>&2 + ;; +--- a/python/check_python.py ++++ b/python/check_python.py +@@ -1,8 +1,4 @@ +-import os, wxversion +-print("wxversion(s): " + ", ".join(wxversion.getInstalled())) +- +-if os.environ["POL_OS"] != "Mac": +- wxversion.ensureMinimal('2.8') ++import os + + import wx + os._exit(0) +--- a/python/install/DescriptionFetcher.py ++++ b/python/install/DescriptionFetcher.py +@@ -1,4 +1,4 @@ +-import threading, time, string, os, urllib2 ++import threading, time, os, urllib.request + import lib.Variables as Variables + + class DescriptionFetcher(threading.Thread): +@@ -28,7 +28,7 @@ class DescriptionFetcher(threading.Threa + time.sleep(0.5) + self.getDescription_bis = self.getDescription + self.med_miniature = None +- self.cut = string.split(self.getDescription,":") ++ self.cut = self.getDescription.split(":") + if(self.cut[0] == "get"): + self.miniature = self.miniature_defaut + # Description +@@ -38,14 +38,14 @@ class DescriptionFetcher(threading.Threa + # Miniatures + try : + url = os.environ["SITE"]+'/V4_data/repository/screenshot.php?id='+self.getDescription.replace(" ","%20") +- req = urllib2.Request(url) +- handle = urllib2.urlopen(req) ++ req = urllib.request.Request(url) ++ handle = urllib.request.urlopen(req) + screenshot_id=handle.read() + + if(screenshot_id != "0"): + url_s1 = 'http://www.playonlinux.com/images/apps/min/'+screenshot_id +- req = urllib2.Request(url_s1) +- handle = urllib2.urlopen(req) ++ req = urllib.request.Request(url_s1) ++ handle = urllib.request.urlopen(req) + + open(Variables.playonlinux_rep+"/tmp/min"+screenshot_id,"w").write(handle.read()) + self.miniature = Variables.playonlinux_rep+"/tmp/min"+screenshot_id +@@ -53,8 +53,8 @@ class DescriptionFetcher(threading.Threa + else: + try: + url = os.environ["SITE"]+'/V2_data/miniatures/'+self.getDescription.replace(" ","%20") +- req = urllib2.Request(url) +- handle = urllib2.urlopen(req) ++ req = urllib.request.Request(url) ++ handle = urllib.request.urlopen(req) + + open(Variables.playonlinux_rep+"/tmp/min","w").write(handle.read()) + self.miniature = Variables.playonlinux_rep+"/tmp/min" +@@ -69,8 +69,8 @@ class DescriptionFetcher(threading.Threa + # Description + try : + url = os.environ["SITE"]+'/V4_data/repository/get_description.php?id='+self.getDescription.replace(" ","%20") +- req = urllib2.Request(url) +- handle = urllib2.urlopen(req) ++ req = urllib.request.Request(url) ++ handle = urllib.request.urlopen(req) + self.htmlContent = handle.read() + if("No description" in self.htmlContent): + self.htmlContent = ""+_("No description")+"" +@@ -84,8 +84,8 @@ class DescriptionFetcher(threading.Threa + if(screenshot_id != 0): + try: + url_s2 = 'http://www.playonlinux.com/images/apps/med/'+screenshot_id +- req = urllib2.Request(url_s2) +- handle = urllib2.urlopen(req) ++ req = urllib.request.Request(url_s2) ++ handle = urllib.request.urlopen(req) + open(Variables.playonlinux_rep+"/tmp/med"+screenshot_id,"w").write(handle.read()) + + self.med_miniature = Variables.playonlinux_rep+"/tmp/med"+screenshot_id +@@ -99,8 +99,8 @@ class DescriptionFetcher(threading.Threa + # Stars + try : + url = os.environ["SITE"]+'/V4_data/repository/stars.php?n='+self.getDescription.replace(" ","%20") +- req = urllib2.Request(url) +- handle = urllib2.urlopen(req) ++ req = urllib.request.Request(url) ++ handle = urllib.request.urlopen(req) + self.stars = handle.read() + except : + self.stars = "0" +--- a/python/wine_versions/WineVersionsTools.py ++++ b/python/wine_versions/WineVersionsTools.py +@@ -1,5 +1,4 @@ + import os +-import string + + from lib import Variables + +@@ -26,7 +25,7 @@ def GetWineVersion(game): + version = "System" + else: + version = line.replace("PATH=", "").replace("\"", "").replace(Variables.playonlinux_rep, "").replace("//", "/") +- version = string.split(version, "/") ++ version = version.split("/") + version = version[1] + +- return version +\ No newline at end of file ++ return version +--- a/python/configurewindow/ConfigureWindowNotebook.py ++++ b/python/configurewindow/ConfigureWindowNotebook.py +@@ -23,13 +23,13 @@ class ConfigureWindowNotebook(wx.Noteboo + self.changing = True + + def winebash(self, command, new_env=None): +- args = shlex.split(command.encode("utf-8", "replace")) ++ args = shlex.split(command) + if (self.s_isPrefix == True): + subprocess.Popen(["bash", Variables.playonlinux_env + "/bash/winebash", "--prefix", +- self.s_prefix.encode('utf-8', 'replace')] + args, env=new_env) ++ self.s_prefix] + args, env=new_env) + else: + subprocess.Popen( +- ["bash", Variables.playonlinux_env + "/bash/winebash", self.s_title.encode('utf-8', 'replace')] + args, ++ ["bash", Variables.playonlinux_env + "/bash/winebash", self.s_title] + args, + env=new_env) + + def evt_winecfg(self, event): +@@ -74,10 +74,10 @@ class ConfigureWindowNotebook(wx.Noteboo + + if self.s_isPrefix == False: + subprocess.Popen(["bash", Variables.playonlinux_env + "/bash/installpolpackages", +- self.s_title.encode('utf-8', 'replace'), selectedPackage]) ++ self.s_title, selectedPackage]) + else: + subprocess.Popen(["bash", Variables.playonlinux_env + "/bash/installpolpackages", "--prefix", +- self.s_prefix.encode('utf-8', 'replace'), selectedPackage]) ++ self.s_prefix, selectedPackage]) + + def AddGeneralChamp(self, title, shortname, value, num): + self.general_elements[shortname + "_text"] = wx.StaticText(self.panelGeneral, -1, title, +@@ -147,13 +147,13 @@ class ConfigureWindowNotebook(wx.Noteboo + self.configurator_title.SetFont(self.fontTitle) + self.configurator_button = wx.Button(self.panelGeneral, 106, _("Run configuration wizard"), pos=(15, 324)) + +- wx.EVT_TEXT(self, 202, self.setname) +- wx.EVT_TEXT(self, 206, self.setargs) +- wx.EVT_TEXT(self, 204, self.setwinedebug) +- +- wx.EVT_COMBOBOX(self, 203, self.assign) +- wx.EVT_COMBOBOX(self, 205, self.assignPrefix) +- wx.EVT_BUTTON(self, 601, self.Parent.Parent.Parent.WineVersion) ++ self.Bind(wx.EVT_TEXT, self.setname, id=202) ++ self.Bind(wx.EVT_TEXT, self.setargs, id=206) ++ self.Bind(wx.EVT_TEXT, self.setwinedebug, id=204) ++ ++ self.Bind(wx.EVT_COMBOBOX, self.assign, id=203) ++ self.Bind(wx.EVT_COMBOBOX, self.assignPrefix, id=205) ++ self.Bind(wx.EVT_BUTTON, self.Parent.Parent.Parent.WineVersion, id=601) + + def Wine(self, nom): + self.panelWine = wx.Panel(self, -1) +@@ -247,15 +247,15 @@ class ConfigureWindowNotebook(wx.Noteboo + + self.control_texte.SetFont(self.caption_font) + +- wx.EVT_BUTTON(self, 100, self.evt_winecfg) +- wx.EVT_BUTTON(self, 101, self.evt_regedit) +- wx.EVT_BUTTON(self, 102, self.evt_wineboot) +- wx.EVT_BUTTON(self, 103, self.evt_cmd) +- wx.EVT_BUTTON(self, 104, self.evt_taskmgr) +- wx.EVT_BUTTON(self, 105, self.evt_killall) +- wx.EVT_BUTTON(self, 106, self.evt_config) +- wx.EVT_BUTTON(self, 107, self.evt_rep) +- wx.EVT_BUTTON(self, 108, self.evt_control) ++ self.Bind(wx.EVT_BUTTON, self.evt_winecfg, id=100) ++ self.Bind(wx.EVT_BUTTON, self.evt_regedit, id=101) ++ self.Bind(wx.EVT_BUTTON, self.evt_wineboot, id=102) ++ self.Bind(wx.EVT_BUTTON, self.evt_cmd, id=103) ++ self.Bind(wx.EVT_BUTTON, self.evt_taskmgr, id=104) ++ self.Bind(wx.EVT_BUTTON, self.evt_killall, id=105) ++ self.Bind(wx.EVT_BUTTON, self.evt_config, id=106) ++ self.Bind(wx.EVT_BUTTON, self.evt_rep, id=107) ++ self.Bind(wx.EVT_BUTTON, self.evt_control, id=108) + + def Packages(self, nom): + self.panelPackages = wx.Panel(self, -1) +@@ -292,9 +292,9 @@ class ConfigureWindowNotebook(wx.Noteboo + self.PackageButton = wx.Button(self.panelPackages, 98, _("Install"), pos=(20 + 530 - 150, 345), size=(150, 30)) + self.PackageButton.Disable() + +- wx.EVT_TREE_SEL_CHANGED(self, 99, self.package_selected) +- wx.EVT_TREE_ITEM_ACTIVATED(self, 99, self.install_package) +- wx.EVT_BUTTON(self, 98, self.install_package) ++ self.Bind(wx.EVT_TREE_SEL_CHANGED, self.package_selected, id=99) ++ self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.install_package, id=99) ++ self.Bind(wx.EVT_BUTTON, self.install_package, id=98) + + self.AddPage(self.panelPackages, nom) + +@@ -308,22 +308,22 @@ class ConfigureWindowNotebook(wx.Noteboo + def change_Direct3D_settings(self, param): + if (self.s_isPrefix == False): + subprocess.Popen( +- ["bash", Variables.playonlinux_env + "/bash/POL_Command", self.s_title.encode('utf-8', 'replace'), +- "POL_Wine_Direct3D", param, self.display_elements[param].GetValue().encode('utf-8', 'replace')]) ++ ["bash", Variables.playonlinux_env + "/bash/POL_Command", self.s_title, ++ "POL_Wine_Direct3D", param, self.display_elements[param].GetValue()]) + else: + subprocess.Popen(["bash", Variables.playonlinux_env + "/bash/POL_Command", "--prefix", +- self.s_prefix.encode('utf-8', 'replace'), "POL_Wine_Direct3D", param, +- self.display_elements[param].GetValue().encode('utf-8', 'replace')]) ++ self.s_prefix, "POL_Wine_Direct3D", param, ++ self.display_elements[param].GetValue()]) + + def change_DirectInput_settings(self, param): + if (self.s_isPrefix == False): + subprocess.Popen( +- ["bash", Variables.playonlinux_env + "/bash/POL_Command", self.s_title.encode('utf-8', 'replace'), +- "POL_Wine_DirectInput", param, self.display_elements[param].GetValue().encode('utf-8', 'replace')]) ++ ["bash", Variables.playonlinux_env + "/bash/POL_Command", self.s_title, ++ "POL_Wine_DirectInput", param, self.display_elements[param].GetValue()]) + else: + subprocess.Popen(["bash", Variables.playonlinux_env + "/bash/POL_Command", "--prefix", +- self.s_prefix.encode('utf-8', 'replace'), "POL_Wine_DirectInput", param, +- self.display_elements[param].GetValue().encode('utf-8', 'replace')]) ++ self.s_prefix, "POL_Wine_DirectInput", param, ++ self.display_elements[param].GetValue()]) + + def get_current_settings(self, param): + self.display_elements[param].SetValue(self.settings[param]) +@@ -373,7 +373,7 @@ class ConfigureWindowNotebook(wx.Noteboo + self.configurator_title.Hide() + self.configurator_button.Hide() + self.configurator_title.SetLabel( +- "{0} specific configuration".format(self.s_title.encode('utf-8', 'replace'))) ++ "{0} specific configuration".format(self.s_title)) + self.display_elements["pre_run_panel"].Show() + self.display_elements["pre_run_text"].Show() + else: +@@ -445,17 +445,17 @@ class ConfigureWindowNotebook(wx.Noteboo + if (param == 402): + if (self.s_isPrefix == False): + playonlinux.open_folder(self.s_title, +- self.display_elements["open_in"].GetValue().encode("utf-8", "replace")) ++ self.display_elements["open_in"].GetValue()) + else: + playonlinux.open_folder_prefix(self.s_prefix) + if (param == 404): + if (self.s_isPrefix == False): + subprocess.Popen( +- ["bash", Variables.playonlinux_env + "/bash/POL_Command", self.s_title.encode('utf-8', 'replace'), +- "POL_OpenShell", self.s_title.encode('utf-8', 'replace')]) ++ ["bash", Variables.playonlinux_env + "/bash/POL_Command", self.s_title, ++ "POL_OpenShell", self.s_title]) + else: + subprocess.Popen(["bash", Variables.playonlinux_env + "/bash/POL_Command", "--prefix", +- self.s_prefix.encode('utf-8', 'replace'), "POL_OpenShell"]) ++ self.s_prefix, "POL_OpenShell"]) + + if (param == 405): + self.FileDialog = wx.FileDialog(self) +@@ -466,22 +466,22 @@ class ConfigureWindowNotebook(wx.Noteboo + self.FileDialog.SetWildcard(self.supported_files) + self.FileDialog.ShowModal() + if (self.FileDialog.GetPath() != ""): +- filename = self.FileDialog.GetPath().encode("utf-8", "replace") ++ filename = self.FileDialog.GetPath() + dirname = os.path.dirname(filename) + if (self.s_isPrefix == True): + subprocess.Popen(["bash", Variables.playonlinux_env + "/bash/POL_Command", "--prefix", +- self.s_prefix.encode('utf-8', 'replace'), "POL_AutoWine", filename], cwd=dirname) ++ self.s_prefix, "POL_AutoWine", filename], cwd=dirname) + else: + subprocess.Popen(["bash", Variables.playonlinux_env + "/bash/POL_Command", +- self.s_title.encode('utf-8', 'replace'), "POL_AutoWine", filename], cwd=dirname) ++ self.s_title, "POL_AutoWine", filename], cwd=dirname) + + if (param == 201): + if (self.s_isPrefix == False): + subprocess.Popen(["bash", Variables.playonlinux_env + "/bash/POL_Command", "--init", +- self.s_title.encode('utf-8', 'replace'), "POL_SetupWindow_shortcut_creator"]) ++ self.s_title, "POL_SetupWindow_shortcut_creator"]) + else: + subprocess.Popen(["bash", Variables.playonlinux_env + "/bash/POL_Command", "--init", "--prefix", +- self.s_prefix.encode('utf-8', 'replace'), "POL_SetupWindow_shortcut_creator"]) ++ self.s_prefix, "POL_SetupWindow_shortcut_creator"]) + + def AddDisplayElement(self, title, shortname, elements, wine, num): + elements.insert(0, "Default") +@@ -494,7 +494,7 @@ class ConfigureWindowNotebook(wx.Noteboo + pos=(300, 17 + num * 40), size=elemsize) + self.display_elements[shortname].AppendItems(wine) + self.display_elements[shortname].SetValue(wine[0]) +- wx.EVT_COMBOBOX(self, 300 + num, self.change_settings) ++ self.Bind(wx.EVT_COMBOBOX, self.change_settings, id=300 + num) + + def AddMiscElement(self, title, shortname, elements, wine, num): + elements.insert(0, "Default") +@@ -506,20 +506,20 @@ class ConfigureWindowNotebook(wx.Noteboo + pos=(300, 17 + num * 40), size=elemsize) + self.display_elements[shortname].AppendItems(wine) + self.display_elements[shortname].SetValue(wine[0]) +- wx.EVT_COMBOBOX(self, 400 + num, self.change_settings) ++ self.Bind(wx.EVT_COMBOBOX, self.change_settings, id=400 + num) + + def AddMiscButton(self, title, shortname, num): + self.display_elements[shortname + "_button"] = wx.Button(self.panelMisc, 400 + num, "", pos=(15, 19 + num * 40), + size=(500, 30)) + self.display_elements[shortname + "_button"].SetLabel(title) + +- wx.EVT_BUTTON(self, 400 + num, self.misc_button) ++ self.Bind(wx.EVT_BUTTON, self.misc_button, id=400 + num) + + def AddMiscChamp(self, title, shortname, value, num): + self.display_elements[shortname + "_text"] = wx.StaticText(self.panelMisc, -1, title, pos=(15, 24 + num * 40)) + self.display_elements[shortname] = wx.TextCtrl(self.panelMisc, 400 + num, value, pos=(245, 19 + num * 40), + size=(270, 25)) +- wx.EVT_TEXT(self, 400 + num, self.set_open_in) ++ self.Bind(wx.EVT_TEXT, self.set_open_in, id=400 + num) + + def set_open_in(self, event): + new_open_in = self.display_elements["open_in"].GetValue() +@@ -538,10 +538,10 @@ class ConfigureWindowNotebook(wx.Noteboo + self.display_elements[shortname] = wx.TextCtrl(self.display_elements[shortname + "_panel"], 400 + num, content, + size=wx.Size(448, 68), pos=(2, 2), + style=Variables.widget_borders | wx.TE_MULTILINE) +- wx.EVT_TEXT(self, 400 + num, self.edit_shortcut) ++ self.Bind(wx.EVT_TEXT, self.edit_shortcut, id=400 + num) + + def edit_shortcut(self, event): +- content = self.display_elements["pre_run"].GetValue().encode("utf-8", "replace") ++ content = self.display_elements["pre_run"].GetValue() + open(os.environ["POL_USER_ROOT"] + "/configurations/pre_shortcut/" + self.s_title, 'w').write(content) + + def AddGeneralButton(self, title, shortname, num): +@@ -549,7 +549,7 @@ class ConfigureWindowNotebook(wx.Noteboo + pos=(15, 9 + num * 40), size=(520, 30)) + self.general_elements[shortname + "_button"].SetLabel(title) + +- wx.EVT_BUTTON(self, 200 + num, self.misc_button) ++ self.Bind(wx.EVT_BUTTON, self.misc_button, id=200 + num) + + def Display(self, nom): + self.display_elements = {} +--- a/python/setupwindow/Downloader.py ++++ b/python/setupwindow/Downloader.py +@@ -1,5 +1,5 @@ + import threading +-import urllib ++import urllib.request + + class Downloader(threading.Thread): + def __init__(self, url, local): +@@ -20,10 +20,10 @@ class Downloader(threading.Thread): + + def download(self): + try: +- urllib.urlretrieve(self.url, self.local, reporthook = self.onHook) ++ urllib.request.urlretrieve(self.url, self.local, reporthook = self.onHook) + except Exception as e: + self.failed = True + self.finished = True + + def run(self): +- self.download() +\ No newline at end of file ++ self.download() +--- a/playonlinux-pkg ++++ b/playonlinux-pkg +@@ -31,7 +31,7 @@ then + CURDIR="$(pwd)" + export MACHTYPE + cd "$WorkingDirectory" +- exec python "$CURDIR/python/wrapper.py" "$CURDIR/bash/$(basename "$0")" "$@" ++ exec python3 "$CURDIR/python/wrapper.py" "$CURDIR/bash/$(basename "$0")" "$@" + else + exec bash "$PLAYONLINUX/bash/$(basename "$0")" "$@" + fi +--- a/playonlinux-shell ++++ b/playonlinux-shell +@@ -42,7 +42,7 @@ then + CURDIR="$(pwd)" + export MACHTYPE + cd "$WorkingDirectory" +- exec python "$CURDIR/python/wrapper.py" "$CURDIR/bash/$(basename "$0")" "$@" ++ exec python3 "$CURDIR/python/wrapper.py" "$CURDIR/bash/$(basename "$0")" "$@" + else + exec bash "$PLAYONLINUX/bash/$(basename "$0")" "$@" + fi +--- a/playonlinux-url_handler ++++ b/playonlinux-url_handler +@@ -44,7 +44,7 @@ then + CURDIR="$(pwd)" + export MACHTYPE + cd "$WorkingDirectory" +- exec python "$CURDIR/python/wrapper.py" "$CURDIR/bash/$(basename "$0")" "$@" ++ exec python3 "$CURDIR/python/wrapper.py" "$CURDIR/bash/$(basename "$0")" "$@" + else + exec bash "$PLAYONLINUX/bash/$(basename "$0")" "$@" + fi diff -Nru playonlinux-4.3.4/debian/patches/series playonlinux-4.3.4/debian/patches/series --- playonlinux-4.3.4/debian/patches/series 2017-07-25 17:46:22.000000000 +0000 +++ playonlinux-4.3.4/debian/patches/series 2020-05-11 15:48:43.000000000 +0000 @@ -1,3 +1,4 @@ set_debian_env.diff remove_binary_plugin.diff typo.diff +python3.patch diff -Nru playonlinux-4.3.4/debian/README.Debian playonlinux-4.3.4/debian/README.Debian --- playonlinux-4.3.4/debian/README.Debian 2017-07-09 17:23:16.000000000 +0000 +++ playonlinux-4.3.4/debian/README.Debian 2020-05-11 15:48:43.000000000 +0000 @@ -8,12 +8,12 @@ PlayOnLinux for Debian ----------------- -To comply with the Debian main category, automatic installation of -Microsoft fonts is deactivated in PlayOnLinux. If you encounter any fonts -troubles in PlayOnLinux, you should think about installing the Debian package +To comply with the Debian main category, automatic installation of +Microsoft fonts is deactivated in PlayOnLinux. If you encounter any fonts +troubles in PlayOnLinux, you should think about installing the Debian package ttf-msttcorefonts-installer. -Alternativelly, you can install these fonts manually in the PlayOnLinux +Alternativelly, you can install these fonts manually in the PlayOnLinux fashion by launching in the PlayOnLinux Debug console : install_fonts diff -Nru playonlinux-4.3.4/debian/rules playonlinux-4.3.4/debian/rules --- playonlinux-4.3.4/debian/rules 2020-03-31 18:44:40.000000000 +0000 +++ playonlinux-4.3.4/debian/rules 2020-05-11 15:48:43.000000000 +0000 @@ -10,10 +10,10 @@ #export DH_VERBOSE=1 %: - dh $@ --with python2 + dh $@ --with python3 override_dh_auto_build-indep: convert -monitor -resize 32x32 $(CURDIR)/etc/playonlinux.png $(CURDIR)/playonlinux.xpm override_dh_auto_install: - +