歌者文明清理员 发表于 2023-5-1 07:47:38

为何此文本在右上角

代码太长,我没写注释,问chatgpt他说我代码太长不肯回我{:10_266:}
import pygame
from selenium import webdriver
from requests import get
from subprocess import getstatusoutput as _gsout
from threading import Thread, Timer
from os import remove
from os.path import isfile
from pickle import load, dump
from time import sleep
from bs4 import BeautifulSoup
from lxml import html

pygame.init()
pygame.key.set_repeat(1000, 50)

class Button(pygame.sprite.Sprite):
    def __init__(self, name, size, font: pygame.font.Font, bgc, fgc, pos, key='\0'):
      pygame.sprite.Sprite.__init__(self)
      self.image = pygame.Surface(size)
      self.image.fill(bgc)
      surf = font.render(name, False, fgc)
      rect = surf.get_rect()
      rect.center = self.image.get_rect().center
      self.image.blit(surf, rect)
      self.rect = self.image.get_rect()
      self.rect.topleft = pos
      self.key = key
      self.font = font
      self.bgc = bgc
      self.fgc = fgc
   
    def chosen(self, event: pygame.event.Event):
      if event.type == pygame.MOUSEBUTTONUP:
            if self.rect.collidepoint(event.pos):
                return True
      if event.type == pygame.KEYDOWN:
            if event.key == ord(self.key) and event.mod & pygame.KMOD_ALT:
                return True
      return False

    def draw(self):
      screen.blit(self.image, self.rect)

class Text(pygame.sprite.Sprite):
    def __init__(self, text, font, color, pos):
      pygame.sprite.Sprite.__init__(self)
      self.font = font
      self._text = text
      self.color = color
      self.image = self.font.render(self.text, False, self.color)
      self.rect = self.image.get_rect()
      self.rect.topleft = pos
   
    def draw(self):
      screen.blit(self.image, self.rect)

    @property
    def text(self):
      return self._text

    @text.setter
    def text(self, new):
      self._text = new
      self.image = self.font.render(self.text, False, self.color)

class Entry(pygame.sprite.Sprite):
    def __init__(self, size, font, bgc, bdc, fgc, pos):
      pygame.sprite.Sprite.__init__(self)
      self.bgc = bgc
      self.bdc = bdc
      self.image = pygame.Surface(size)
      self.image.fill(bgc)
      for x in range(0, size):
            self.image.set_at((x, 0), bdc)
            self.image.set_at((x, size - 1), bdc)
      for y in range(0, size):
            self.image.set_at((0, y), bdc)
            self.image.set_at((size - 1, y), bdc)
      self.text = Text('', font, fgc, pos)
      self.image.blit(self.text.image, (0, 0))
      self.rect = self.image.get_rect()
      self.rect.topleft = pos
      self.selected = False
   
    @property
    def fgc(self):
      return self.text.color

    def draw(self):
      screen.blit(self.image, self.rect)
   
    def listen(self, event):
      if event.type == pygame.MOUSEBUTTONDOWN:
            self.selected = self.rect.collidepoint(event.pos)
      elif event.type == pygame.KEYDOWN:
            if self.selected:
                if event.key == pygame.K_BACKSPACE:
                  self.text.text = self.text.text[:-1]
                  self._update_text()
                elif (event.unicode in
                  'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()_+[];,./{}|:"<>?'
                  + "'" + '\\'
                ):
                  self.text.text += event.unicode
                  self._update_text()
   
    def _update_text(self):
      size = self.rect.size
      self.image = pygame.Surface(size)
      self.image.fill(self.bgc)
      for x in range(0, size):
            self.image.set_at((x, 0), self.bdc)
            self.image.set_at((x, size - 1), self.bdc)
      for y in range(0, size):
            self.image.set_at((0, y), self.bdc)
            self.image.set_at((size - 1, y), self.bdc)
      self.text = Text(self.text.text, self.text.font, self.fgc, self.rect.topleft)
      self.image.blit(self.text.image, (0, 0))

class PasswordEntry(Entry):
    def _update_text(self):
      size = self.rect.size
      self.image = pygame.Surface(size)
      self.image.fill(self.bgc)
      for x in range(0, size):
            self.image.set_at((x, 0), self.bdc)
            self.image.set_at((x, size - 1), self.bdc)
      for y in range(0, size):
            self.image.set_at((0, y), self.bdc)
            self.image.set_at((size - 1, y), self.bdc)
      self.text = Text(len(self.text.text) * '*', self.text.font, self.fgc, self.rect.topleft)
      self.image.blit(self.text.image, (0, 0))

class MyThread(Thread):
    def __init__(self, func, args=(), kwargs=None, timeout=None):
      Thread.__init__(self)
      self.func = func
      self.args = args
      self.kwargs = {} if kwargs is None else kwargs
      self.timeout = timeout
      self.result = None
   
    def run(self):
      self.result = self.func(*self.args, **self.kwargs)

def timeout(seconds):
    def decorated(func):
      def done(*args, **kwargs):
            err = None
            result = None
            def inner():
                nonlocal err, result
                try:
                  result = func(*args, **kwargs)
                except BaseException as msg:
                  err = msg
            thread = Thread(target=inner)
            thread.start()
            # 只等待seconds秒
            thread.join(seconds)
            # 如果线程还活着
            if thread.is_alive():
                raise TimeoutError
            if err:
                raise err
            return result
      return done
    return decorated

# 实际上是生成了一个新的decorated函数,通过闭包的形式保存了秒数,然后done里再调用inner
@timeout(1)
def soutf(msg):
    return _gsout(msg)

def check_internet():
    try:
      soutf('ping fishc.com.cn')
      # 这东西,报错可是最快
      print('你没联网')
      exit()
    except TimeoutError:
      # 连上了的话那就好说
      pass

def load_image(url):
    filename = 'temp.png'
    with open(filename, 'wb') as f:
      f.write(get(url, headers={
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36'
      }).content)
    image = pygame.image.load(filename)
    remove(filename)
    return image

def save():
    with open('fishc.set', 'wb') as f:
      dump(data, f)

def loginf(email, password):
    driver.get('https://fishc.com.cn/member.php?mod=logging&action=login')
    sleep(1)
    driver.find_element(
      'xpath',
      '/html/body/div/div/div/div/div/div/div/form/div/div/table/tbody/tr/td/input'
    ).send_keys(email)
    driver.find_element(
      'xpath',
      '/html/body/div/div/div/div/div/div/div/form/div/div/table/tbody/tr/td/input'
    ).send_keys(password)
    driver.find_element(
      'xpath',
      '/html/body/div/div/div/div/div/div/div/form/div/div/table/tbody/tr/td/button'
    ).click()
    sleep(1)
    driver.find_element('id', 'succeedmessage_href').click()
    return driver.page_source

def getdata(source):
    tree = html.fromstring(source)
    part = html.tostring(tree.xpath('//strong[@class="vwmy"]'), encoding='unicode')
    usergroup = html.tostring(tree.xpath('//a[@id="g_upmine"]'), encoding='unicode')
    soup = BeautifulSoup(part, features='lxml')
    soup2 = BeautifulSoup(usergroup, features='lxml')
    return soup.find('a').text, soup2.find('font').get('color'), soup2.find('font').text

def login_to1(email, password):
    return getdata(loginf(email, password))

def login_to(email, password):
    result = login_to1(email, password)
    Timer(3, hide_msg).start()
    return result

def hide_msg():
    global state
    state = 3

check_internet()

size = width, height = (640, 480)
screen = pygame.display.set_mode(size)
logo = load_image('https://fishc.com.cn/static/image/common/logo.png')
icon = load_image('https://fishc.com.cn/favicon.ico')
pygame.display.set_caption('FishC App')
pygame.display.set_icon(icon)
font15 = pygame.font.SysFont('microsoftyaheiui', 15)
font20 = pygame.font.SysFont('microsoftyaheiui', 20)
font20b = pygame.font.SysFont('microsoftyaheiui', 20)
font20b.set_bold(True)
chrome = Button('hrome', (100, 50), font20, (0, 255, 0), (0, 0, 0), (100, 200), 'c')
firefox = Button('irefox', (100, 50), font20, (0, 255, 0), (0, 0, 0), (300, 200), 'f')
c_chrome = Button('Change driver(Using Chrome)', (200, 50), font15, (255, 255, 255), (0, 0, 0), (width - 200, 0))
c_firefox = Button('Change driver(Using Firefox)', (200, 50), font15, (255, 255, 255), (0, 0, 0), (width - 200, 0))
change = c_chrome
login = Button('登录(L)', (100, 50), font20, (0, 0, 255), (255, 255, 255), (width - 123, height - 100), 'l')
save1 = Button('保存数据(S)', (150, 50), font20, (0, 0, 255), (255, 255, 255), (width // 2 - 125, height // 2 - 50), 's')
unsave = Button('不保存数据(N)', (150, 50), font20, (0, 0, 255), (255, 255, 255), (width // 2 - 125, height // 2 + 50), 'n')
swin = pygame.Rect(width // 4, height // 4, width // 2, height // 2)
username = Entry((200, 30), font20, (255, 255, 255), (0, 0, 127), (0, 0, 0), (200, 100))
pwd = Entry((200, 30), font20, (255, 255, 255), (0, 0, 127), (0, 0, 0), (200, 180))
t_user = Text('用户名/邮箱', font20b, (0, 0, 0), (90, 100))
t_pwd = Text('密码', font20b, (0, 0, 0), (100, 180))
logining = Text('', font15, (0, 0, 0), (300, 200))
succed = Text('', font15, (0, 0, 0), (width - 150, 10))

data = {
    'driver': None,
    'username': None,
    'password': None
}
state = 0
if isfile('fishc.set'):
    with open('fishc.set', 'rb') as f:
      data = load(f)
thread = None

clock = pygame.time.Clock()
running = True
while running:
    clock.tick(30)
    screen.fill((255, 255, 255))
    screen.blit(logo, (0, 0))
    if state == 1:
      pygame.draw.rect(screen, (0, 0, 0), swin, 2)
      save1.draw()
      unsave.draw()
    elif state == 0:
      if data['driver'] is None:
            chrome.draw()
            firefox.draw()
      else:
            change.draw()
            if data['username'] is None or data['password'] is None:
                username.draw()
                t_user.draw()
                pwd.draw()
                t_pwd.draw()
                login.draw()
    elif state == 2:
      if thread.result is None:
            logining.text = '登录中...'
      else:
            logining.text = '登录成功!'
      logining.draw()
    elif state == 3:
      succed.text = f'{thread.result}(用户组: {thread.result})'
      succed.color = thread.result
      succed.rect.topleft = (width - succed.rect.width - 10, 10)
      succed.draw()
    for event in pygame.event.get():
      if event.type == pygame.QUIT:
            if state == 1:
                running = False
                # 虽然只会退出for
                break
            state = 1
      if state == 1:
            if save1.chosen(event):
                save()
                running = False
            elif unsave.chosen(event):
                running = False
      else:
            if data['driver'] is None:
                temp = False
                if chrome.chosen(event):
                  data['driver'] = (webdriver.Chrome, webdriver.ChromeOptions)
                  change = c_chrome
                  temp = True
                elif firefox.chosen(event):
                  data['driver'] = (webdriver.Firefox, webdriver.FirefoxOptions)
                  change = c_firefox               
                  temp = True
                if temp:
                  option = data['driver']()
                  option.add_argument('headless')
                  driver = data['driver'](options=option)
            else:
                if data['username'] is None or data['password'] is None:
                  username.listen(event)
                  pwd.listen(event)
                if change.chosen(event):
                  data['driver'] = None
                if login.chosen(event):
                  data['username'] = username.text.text
                  data['password'] = pwd.text.text
                  state = 2
                  thread = MyThread(func=login_to, args=(username.text.text, pwd.text.text))
                  thread.start()
    pygame.display.flip()
pygame.quit()
driver.close()

就这么一个文件

picture:

isdkz 发表于 2023-5-1 07:56:01

从您提供的代码来看,登录成功后,用户名显示在右上角是由以下代码实现的:

succed = Text('', font15, (0, 0, 0), (width - 150, 10))


这行代码创建了一个名为succed的Text对象,将其初始位置设置为屏幕右上角附近。具体来说, width - 150使文本距离屏幕右侧 150 像素, 10则是距离屏幕顶部的像素数。当登录成功时, state变为 3,下面的代码将用户名和用户组信息显示在屏幕上:

elif state == 3:
    succed.text = f'{thread.result}(用户组: {thread.result})'
    succed.color = thread.result
    succed.rect.topleft = (width - succed.rect.width - 10, 10)
    succed.draw()


在这段代码中,首先更新succed的文本属性,包括用户名和用户组信息。接着,根据文本的宽度,重新计算并设置文本的位置,使其始终保持在屏幕右上角附近。最后调用succed.draw()方法将文本绘制在屏幕上。

歌者文明清理员 发表于 2023-5-1 08:00:28

isdkz 发表于 2023-5-1 07:56
从您提供的代码来看,登录成功后,用户名显示在右上角是由以下代码实现的:




似乎并没有回答问题

isdkz 发表于 2023-5-1 08:03:21

歌者文明清理员 发表于 2023-5-1 08:00
似乎并没有回答问题

要将用户名显示在屏幕正中间,首先需要在每次更新文本内容时计算正确的位置。可以通过以下方法调整代码来实现:

1. 修改succed对象的初始化位置,使其在屏幕正中间。这里,我们将其位置设置为(width // 2, height // 2) :

succed = Text('', font15, (0, 0, 0), (width // 2, height // 2))


2. 在登录成功时,更新succed对象的位置,使其始终保持在屏幕正中间:

elif state == 3:
    succed.text = f'{thread.result}(用户组: {thread.result})'
    succed.color = thread.result
    succed.rect.center = (width // 2, height // 2)
    succed.draw()


这里,我们将succed.rect.topleft替换为succed.rect.center ,并将其位置设置为屏幕中心点(width // 2, height // 2) 。这样,在登录成功后,用户名和用户组信息将显示在屏幕正中间。

歌者文明清理员 发表于 2023-5-1 08:09:29

isdkz 发表于 2023-5-1 08:03
要将用户名显示在屏幕正中间,首先需要在每次更新文本内容时计算正确的位置。可以通过以下方法调整代码来 ...



也不是正中间啊

isdkz 发表于 2023-5-1 08:14:32

歌者文明清理员 发表于 2023-5-1 08:09
也不是正中间啊

抱歉,看起来是文本的渲染方式导致的。为了使文本更精确地居中,可以在绘制文本前计算文本宽度的一半,并将其减去屏幕宽度的一半。这样可以确保文本在屏幕中心位置上。修改代码如下:

1. 在Text类中添加一个方法draw_centered ,用于计算并绘制居中的文本:

class Text:
    # 省略其他代码...

    def draw_centered(self, surface):
      self.surface = self.font.render(self.text, True, self.color)
      self.rect = self.surface.get_rect()
      self.rect.center = (width // 2, height // 2)
      surface.blit(self.surface, self.rect.topleft)


2. 更新state == 3的情况,使用新的draw_centered方法绘制居中的文本:

elif state == 3:
    succed.text = f'{thread.result}(用户组: {thread.result})'
    succed.color = thread.result
    succed.draw_centered(screen)


这样,当登录成功后,用户名和用户组信息将准确显示在屏幕正中间。

歌者文明清理员 发表于 2023-5-1 08:35:37

isdkz 发表于 2023-5-1 08:14
抱歉,看起来是文本的渲染方式导致的。为了使文本更精确地居中,可以在绘制文本前计算文本宽度的一半,并 ...

看你的代码知道为什么了

Threebody1 发表于 2023-5-1 08:44:59

isdkz 发表于 2023-5-1 08:14
抱歉,看起来是文本的渲染方式导致的。为了使文本更精确地居中,可以在绘制文本前计算文本宽度的一半,并 ...

是因为text.setter里更新text的时候没更新rect
页: [1]
查看完整版本: 为何此文本在右上角