# -*- coding:utf-8 -*-
import configparser
import os
import pathlib
import re
import subprocess
import sys
import time
from configparser import ExtendedInterpolation
# import winreg
from subprocess import Popen
import psutil
import pymysql
import requests
from PyQt5.QtCore import pyqtSignal, QThread
from PyQt5.QtGui import QTextCursor
from PyQt5.QtWidgets import QApplication, QMainWindow, QMessageBox
# 界面文件为 mainui.py
from mainui import *
configFile = 'config.ini'
# 创建配置文件对象
config = configparser.ConfigParser(interpolation=ExtendedInterpolation(), inline_comment_prefixes=['#', ';'], allow_no_value=True)
# 读取配置文件
config.read(configFile, encoding='utf-8')
class MainWindow(QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setupUi(self)
self.apktool_path = config['TOOL_PATH']['apktool_path']
self.android_name = config['ANDROID']['android_name']
self.android_name_file = config['ANDROID']['android_name_file']
self.tmp_android_name = config['ANDROID']['tmp_android_name']
self.new_android_name = config['ANDROID']['new_android_name']
# 初始赋值
self.nowIpLabel.setText(config['GAME']['now_ip'])
self.newIpInput.setText(config['GAME']['new_ip'])
self.nowGameLabel.setText(config['GAME']['game_name'])
self.newGameInput.setText(config['GAME']['game_name'])
# 设置按钮状态
if not config.has_section('NGINX'):
self.nginxButton.setDisabled(True)
else:
if isinstance(checkprocess("nginx.exe"), int):
self.nginxButton.setText("关闭Nginx")
else:
self.nginxButton.setText("启动Nginx")
if not config.has_section('MYSQL'):
self.mysqlButton.setDisabled(True)
else:
if isinstance(checkprocess("mysqld.exe"), int):
self.mysqlButton.setText("关闭MySql")
else:
self.mysqlButton.setText("启动MySql")
if not config.has_section('WEB'):
self.gmurlButton.setDisabled(True)
self.yyurlButton.setDisabled(True)
else:
self.gmurlButton.setToolTip("
网址:" + config['WEB']['gm_url'] + "
帐号:" + config['WEB']['gm_user'] + "
密码:" + config['WEB']['gm_pass'] + "
校验码:" + config['WEB']['gm_code'] + "
")
self.yyurlButton.setToolTip("网址:" + config['WEB']['game_url'] + "
帐号:" + config['WEB']['game_user'] + "
密码:" + config['WEB']['game_pass'] + "
校验码:" + config['WEB']['game_code'] + "
")
if config.has_section('ANDROID'):
self.fanbianyiButton.setToolTip("反编译的APK路径:
" + config['ANDROID']['android_name'] + "
反编译后的文件路径:
" + config['ANDROID']['android_name_file'])
self.shengchengButton.setToolTip("生成的APK路径:
" + config['ANDROID']['tmp_android_name'])
self.qianmingButton.setToolTip("签名后的APK路径:
" + config['ANDROID']['new_android_name'] + "
密钥路径:
" + config['TOOL_PATH']['apktool_path'] + "key.keystore")
# 初始化JAVA配置
if not os.getenv('JAVA_HOME'):
java_path = config['TOOL_PATH']['java_path']
os.environ['JAVA_HOME'] = java_path
os.environ['path'] = os.getenv('path') + java_path + 'bin;' + java_path + 'jre\\bin;'
os.environ['CLASSPATH'] = java_path + 'bin;' + java_path + 'lib\\dt.jar;' + java_path + 'lib\\tools.jar;'
# print(os.getenv('JAVA_HOME'))
# print(os.getenv('CLASSPATH'))
# print(os.getenv('path'))
def saveSet(self, event):
print('保存设置,按钮被单击')
message = QMessageBox.information(self, "提示信息", "确定要保存设置吗?", QMessageBox.Ok | QMessageBox.No, QMessageBox.No)
if message == QMessageBox.No:
return
# 取值
now_ip = config['GAME']['now_ip']
new_ip = self.newIpInput.text()
now_game = config['GAME']['game_name']
new_game = self.newGameInput.text()
compile_ip = re.compile('^(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|[1-9])\.(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\d)\.(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\d)\.(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\d)$')
# 验证IP地址格式
if (new_ip == "") or new_game == "" or not (compile_ip.match(new_ip)):
QMessageBox.information(self, "提示信息", "IP地址、游戏名称为空或有误,请正确填写IPv4地址或游戏名称!")
return
# 处理SQL语句
if config.has_section('GAME_DATA_SQL'):
sqls = []
for key, value in config.items('GAME_DATA_SQL'):
print(key, value)
sql = value.replace('~【game_ip】~', new_ip)
sql = sql.replace('~【game_name】~', new_game)
sqls.append(sql)
print(key, sql)
# 判断MYSQL是否启动
if sqls:
if isinstance(checkprocess("mysqld.exe"), int):
mysql_run(sqls)
else:
message = QMessageBox.information(self, "提示信息", "MYSQL尚未启动,数据库相关脚本无法执行,是否现在启动MYSQL?", QMessageBox.Ok | QMessageBox.No, QMessageBox.Ok)
if message == QMessageBox.Ok:
self.mysql(self.mysqlButton) # 触发MYSQL启动按钮
sum = 0
# 以0.5秒的频次检测MYSQL是否启动,超过20次则超时
while not isinstance(checkprocess("mysqld.exe"), int):
if sum > 20:
QMessageBox.information(self, "提示信息", "MYSQL启动失败,请重新尝试!")
return
time.sleep(0.5)
sum = sum + 1
mysql_run(sqls)
else:
return
# 配置文件处理
sum = 0
for key, value in config.items('GAME_FILE_PATH'):
print(key, value)
# 处理文件编码
if len(value.split(",")) < 2:
code = config['GAME']['file_encoding']
else:
code = value.split(",")[1]
filename = value.split(",")[0]
# 执行文件内容替换
try:
with open(filename, 'rt+', encoding=code) as f:
t = f.read()
t = t.replace(now_ip, new_ip)
t = t.replace(now_game, new_game)
# 读写偏移位置移到最开始处
f.seek(0, 0)
f.write(t)
# 设置文件结尾 EOF
f.truncate()
sum = sum + 1
except:
QMessageBox.information(self, "提示信息", "替换“" + filename + "”出错,可能文件不存在或编码不对,请检查需要替换的文件编码与配置文件编码是否相符!")
return
# 写入配置文件
config.set('GAME', 'game_name', new_game)
config.set('GAME', 'now_ip', new_ip)
config.set('GAME', 'new_ip', new_ip)
saveconfig = open(configFile, 'wt', encoding=config['GAME']['file_encoding'])
config.write(saveconfig) # 把要修改的节点的内容写到文件中
saveconfig.close()
# 设置按钮文字变化
self.nowGameLabel.setText(new_game)
self.nowIpLabel.setText(new_ip)
# 成功提示
QMessageBox.information(self, "提示信息", "配置保存成功!重启游戏后生效!
游戏名称:" + new_game + "----游戏IP:" + new_ip + "
共配置了" + str(sum) + "个文件")
print('修改完成:' + new_game + ':' + new_ip)
def nginx(self, event):
if self.nginxButton.text() == '启动Nginx':
Popen('start .\\nginx.exe',
shell=True,
cwd=config['NGINX']['nginx_path'],
# encoding='utf-8'
)
Popen(
config['PHP']['php_spawner_path'] + 'php-cgi-spawner.exe "' + config['PHP']['php_path'] + 'php-cgi.exe -c ' + config['PHP']['php_path'] + 'php.ini" ' + config['PHP']['php_cgi_port'] + ' 1+16',
shell=True,
# cwd=config['GAME']['dir'] + 'phpstudy_pro',
# encoding='utf-8'
)
self.nginxButton.setText("关闭Nginx")
else:
nginx_process = Popen('.\\nginx.exe -s stop',
shell=True,
cwd=config['NGINX']['nginx_path'],
# encoding='utf-8'
)
spawner_process = Popen('taskkill /f /t /im php-cgi-spawner.exe',
shell=True,
# encoding='utf-8'
)
PHP_process = Popen('taskkill /f /t /im php-cgi.exe',
shell=True,
# encoding='utf-8'
)
self.nginxButton.setText("启动Nginx")
# print(process)
# os.chdir("D:\\LYserver\\phpstudy_pro\\Extensions\\Nginx1.15.11")
# nginxtype = os.system('start .\\nginx.exe') # 不弹出界面
# if nginxtype
# os.system('start .\\nginx.exe') #弹出界面
print('启动Nginx,按钮被单击')
def mysql(self, event):
if self.mysqlButton.text() == '启动MySql':
Popen('.\\mysqld.exe',
shell=True,
cwd=config['MYSQL']['mysql_path'] + 'bin',
# encoding='utf-8'
)
self.mysqlButton.setText("关闭MySql")
else:
Popen('taskkill /f /t /im mysqld.exe',
shell=True,
# encoding='utf-8'
)
self.mysqlButton.setText("启动MySql")
print('启动MySql,按钮被单击')
def game(self, event):
if self.gameButton.text() == '启动游戏':
# 读取游戏启动路径
for key, value in config.items('GAME_PATH'):
print(key, value)
# 处理启动时间
if len(value.split(",")) < 2:
sleeptime = 0
else:
sleeptime = float(value.split(",")[1])
game_file = value.split(",")[0]
try:
Popen('start ' + game_file,
shell=True,
# cwd=values,
# encoding='utf-8'
)
time.sleep(float(sleeptime)) # 延迟启动时间
except:
QMessageBox.information(self, "提示信息", "启动“" + game_file + "”出错,可能文件不存在或配置不对!")
return
self.gameButton.setText("关闭游戏")
else:
# 读取游戏启动路径
for key, value in config.items('GAME_PATH'):
print(key, value)
# 获取进程名称并结束进程
game_file = value.split(",")[0].split("\\")
game_file = game_file[len(game_file) - 1]
try:
Popen(
'taskkill /f /t /im ' + game_file,
shell=True,
# encoding='utf-8'
)
except:
QMessageBox.information(self, "提示信息", "关闭游戏进程:“" + game_file + "”出错!")
return
# Popen(
# 'taskkill /f /t /im LocalLogServer64_R.exe & taskkill /f /t /im LoggerServer64_R.exe & taskkill /f /t /im SessionServer64_R.exe & taskkill /f /t /im NameServer64_R.exe & taskkill /f /t /im LogicServerCQ64_R.exe & taskkill /f /t /im GateServer64_R.exe & taskkill /f /t /im DBCenterServer64_R.exe & taskkill /f /t /im DBServer64_R.exe & taskkill /f /t /im BackStageServer64_R.exe & taskkill /f /t /im AMServer64_R.exe',
# shell=True,
# # encoding='utf-8'
# )
self.gameButton.setText("启动游戏")
print('启动游戏,按钮被单击')
def fanbianyi(self, event):
obj = self.fanbianyiButton
# obj.setText('运行中')
title = obj.text()
cmd = 'apktool d ' + self.android_name + ' -f -o ' + self.android_name_file
cwd = self.apktool_path
code = "gbk"
path = self.android_name_file
self.add_cmd_msg(cmd, cwd, title, obj, path, code)
print('反编译APK,按钮被单击')
def shengcheng(self, event):
obj = self.shengchengButton
# obj.setText('运行中')
title = obj.text()
cmd = 'apktool b ' + self.android_name_file + ' -o ' + self.tmp_android_name
cwd = self.apktool_path
code = "gbk"
path = self.tmp_android_name
self.add_cmd_msg(cmd, cwd, title, obj, path, code)
print('生成APK,按钮被单击')
def qianming(self, event):
obj = self.qianmingButton
# obj.setText('运行中')
title = obj.text()
cmd = 'jarsigner -verbose -keystore key.keystore -storepass 123456 -signedjar ' + self.new_android_name + ' ' + self.tmp_android_name + ' key.keystore'
cwd = self.apktool_path
code = "gbk"
path = self.new_android_name
self.add_cmd_msg(cmd, cwd, title, obj, path, code)
print('APK签名,按钮被单击')
def heidisql(self, event):
cmd = 'start "" ' + config['TOOL_PATH']['heidisql_path']
Popen(cmd,
shell=True,
# cwd=config['MYSQL']['mysql_path'] + 'bin',
# encoding='utf-8'
)
print('heidisql,按钮被单击')
def gmurl(self, event):
cmd = 'start ' + config['WEB']['gm_url']
Popen(cmd,
shell=True,
# cwd=config['MYSQL']['mysql_path'] + 'bin',
# encoding='utf-8'
)
print('gmurl,按钮被单击')
# TODO 增加自动登录GM后台功能
def yyurl(self, event):
cmd = 'start ' + config['WEB']['game_url']
Popen(cmd,
shell=True,
# cwd=config['MYSQL']['mysql_path'] + 'bin',
# encoding='utf-8'
)
print('yyurl,按钮被单击')
# TODO 增加自动登录后台功能
# VC运行库
def msvbcrt(self, event):
cmd = 'start "" ' + config['TOOL_PATH']['msvbcrt_path']
Popen(cmd,
shell=True,
# cwd=config['MYSQL']['mysql_path'] + 'bin',
# encoding='utf-8'
)
print('VC运行库,按钮被单击')
# 程序升级
def up(self, event):
if self.upButton.text() == '检查新版' or self.upButton.text() == '无新版本':
newVersion = getNewVersion() # 获取新版本号
# if os.path.isfile("upgrade.bat"): # 判断是否有upgrade.bat这个文件,有就删除
# os.remove("upgrade.bat")
if version < newVersion: # 判断当前程序是否是最新版本
self.upButton.setText("新版本:" + str(newVersion))
else:
self.upButton.setText("无新版本")
print('当前版本:', version, '\t最新版本:', newVersion)
else:
cmd = 'start ' + 'https://shileiye.com/'
Popen(cmd,
shell=True,
)
print('检查新版,按钮被单击')
# TODO 增加自动下载更新功能
# 利用新线程进行脚本执行回显
def add_cmd_msg(self, cmd, cwd="", title="", obj="", path="", code="utf8"):
obj.setEnabled(False)
self.CMDtextEdit.clear()
self.CMDtextEdit.append(title + "脚本开始执行!")
self.work = Thread(cmd, cwd, title, obj, path, code)
self.work.trigger.connect(self.show_msg) # 线程中的trigger与主类中的方法进行绑定
self.work.daemon = True # 主进程退出则该线程也退出
self.work.start() # 开启线程
# 更新UI方法
def show_msg(self, str):
self.CMDtextEdit.moveCursor(QTextCursor.End)
self.CMDtextEdit.append(str)
# self.CMDtextEdit.insertPlainText(str)
# self.CMDtextEdit.insertHtml(str)
# 线程类
class Thread(QThread):
trigger = pyqtSignal(str) # 注意pyqtSignal一定要实例到__init__前面
def __init__(self, cmd, cwd="", title="", obj="", path="", code="utf8"):
super(Thread, self).__init__()
# 定义的变量
self.cmd = cmd
self.cwd = cwd
self.title = title
self.obj = obj
self.path = path
self.code = code
self.state = True
# 执行耗时操作
def run(self):
process = subprocess.Popen(self.cmd, shell=True, cwd=self.cwd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
while process.poll() is None:
line = process.stdout.readline()
line = line.strip()
if line:
self.trigger.emit(line.decode(self.code, 'ignore'))
print(line.decode(self.code, 'ignore'))
self.trigger.emit(self.title + '脚本执行结束!')
self.obj.setEnabled(True)
# self.obj.setText(self.title)
# print(self.obj)
# self.fanbianyiButton.setEnabled(False)
# self.fanbianyiButton.setText('运行中')
if self.path != "":
self.trigger.emit('文件路径:' + self.path)
startfile(self.path)
# MYSQL操作函数
def mysql_run(sqls, table=""):
print(sqls)
# 打开数据库连接
db = pymysql.connect(host=config['MYSQL']['mysql_host'],
user=config['MYSQL']['mysql_user'],
password=config['MYSQL']['mysql_pass'],
database=table,
port=int(config['MYSQL']['mysql_port'])
)
# 使用cursor()方法获取操作游标
cursor = db.cursor()
for sql in sqls:
try:
# 执行SQL语句
cursor.execute(sql)
# 提交到数据库执行
db.commit()
print('更新成功')
except:
print('出错了')
# 发生错误时回滚
db.rollback()
# 关闭数据库连接
db.close()
print('MySql执行完毕')
# 进程判断,返回进程ID
def checkprocess(processname):
pl = psutil.pids()
for pid in pl:
if psutil.Process(pid).name() == processname:
return pid
# 打开文件或文件夹,并且选中文件
def startfile(filename):
try:
os.startfile(f'explorer /select, "{pathlib.Path(filename)}')
except:
subprocess.Popen(f'explorer /select, "{pathlib.Path(filename)}')
# 获取最新程序版本号
def getNewVersion():
url = r'http://127.0.0.1:82/Version.txt'
try:
value = requests.get(url, timeout=10).content.decode()
except:
value = 0
return float(value)
if __name__ == '__main__':
version = 1.0
app = QApplication(sys.argv)
myWin = MainWindow()
myWin.show()
sys.exit(app.exec_())