pygame - 页面向下滚动

5 投票
1 回答
12095 浏览
提问于 2025-04-18 11:47

我的程序简单来说是一个联系人应用,这意味着用户需要在应用里存储多个联系人。这样一来,联系人就会超出屏幕显示范围,所以我需要能够向下滚动页面,这样用户才能看到所有联系人,并与特定的联系人进行互动。

不过,我现在遇到了一些麻烦……我该怎么做呢?我面临的问题是,应用的宽度和高度是固定的,所以我不能向下滚动页面,结果发现背景变成黑色,因为我的矩形只设置成和应用窗口的长度一样长。另一个问题是,我不知道怎么“推动”屏幕向上。我完全没有头绪。

这是我目前的程序代码:

import pickle
import operator
import pygame
import sys
from pygame.locals import *
from itertools import groupby

#create Contact class
class Contact():
    def __init__(self, firstName, lastName, address, groupType,
                 telephone, mobile, email, photoField):
        self.firstName = firstName
        self.lastName = lastName
        self.address = address
        self.groupType = groupType
        self.telephone = telephone
        self.mobile = mobile
        self.email = email
        self.photoField = photoField

    def showDetails(self):
        print("First Name:\t", self.firstName)
        print("Last Name:\t", self.lastName)
        print("Address:\t", self.address)
        print("Telephone:\t", self.telephone)
        print("Mobile:\t", self.mobile)
        print("Email:\t", self.email)

    @classmethod
    def from_input(cls):
        firstName = input("First Name: ")
        lastName = input("Last Name: ")
        address = input("Address: ")
        telephone = input("Telephone: ")
        mobile = input("Mobile: ")
        email = input("Email: ")
        return cls(firstName, lastName, address, None,

                   telephone, mobile, email, None)

class AddressBook():
    def __init__(self):
        self.contactsList = pickle.load(open("save.p", "rb"))

    def addContact(self, contact = None):
        if contact is None:
            contact = Contact.from_input()
        self.contactsList.append(contact)
        pickle.dump(self.contactsList, open("save.p", "wb"))

    def delContact(self, contact = None):
        if contact is None:
            search = input("Search: ")
            for i in self.contactsList:
                if (i.firstName.lower() == search.lower()) or (i.lastName.lower() == search.lower()):
                    indexed = self.contactsList.index(i)
                    del self.contactsList[indexed]
                    pickle.dump(self.contactsList, open("save.p", "wb"))
                elif (i.firstName.lower() != search.lower()) or (i.lastName.lower() != search.lower()):
                    continue

    def contactInfo(self, contact = None):
        if contact is None:
            search = input("Search: ")
            print()

            #display contact information
            for i in self.contactsList:
                if (i.firstName.lower() == search.lower()) or (i.lastName.lower() == search.lower()):
                    i.showDetails()
                    print()
                elif (i.firstName.lower() != search.lower()) or (i.lastName.lower() != search.lower()):
                    continue
                else:
                    print("No contacts\n")

    def contactSearch(self, contact = None):
        if contact is None:
            search = input("Search: ")
            print()

            for i in self.contactsList:
                if (i.firstName.lower() == search.lower()) or (i.lastName.lower() == search.lower()):
                    print(i.firstName, i.lastName)
                    print()
                elif (i.firstName.lower() != search.lower()) or (i.lastName.lower() != search.lower()):
                    continue
                else:
                    print("No contacts\n")

class Page():
    def __init__(self, screen = pygame.display.set_mode((320, 480)), caption = pygame.display.set_caption("Contacts")):
        self.screen = screen
        self.caption = caption

    def style(self):
        pygame.draw.rect(self.screen, (171,0,0), (0,0,320,63), 0)
        pygame.draw.rect(self.screen, (230,230,230), (0,63,320,417), 0)
        pygame.draw.line(self.screen, (120,0,0), (5,61), (320, 61), 2)

class MainPage(Page):
    def __init__(self, screen = pygame.display.set_mode((320, 480)), caption = pygame.display.set_caption("Contacts"), title = "Contacts"):
        Page.__init__(self, screen, caption)
        self.title = title

    def style(self):
        Page.style(self)
        titleFont = pygame.font.SysFont("trebuchet ms", 38)
        textSurface = titleFont.render(self.title, True, (255,255,255))
        self.screen.blit(textSurface, (5, 18))
        AddButton().shape()

    def printContacts(self):
        addressBook = AddressBook()
        addressBook.contactsList
        addressBook.contactsList.sort(key = lambda c: (c.lastName, c.firstName) if c.lastName else (c.firstName, ""))
        contactFont = pygame.font.SysFont("trebuchet ms", 18)
        y = 80

        for (key, g) in groupby(addressBook.contactsList, lambda c: c.lastName[0] if c.lastName else c.firstName[0]):
            groupName = contactFont.render(key, True, (171,0,0))
            self.screen.blit(groupName, (5, y))
            pygame.draw.line(self.screen, (0,0,0), (5,(y+20)), (320, (y+20)), 1)
            y += 30

            for i in g:
                name = i.firstName + " " + i.lastName
                textName = contactFont.render(name, True, (0,0,0))
                pygame.draw.line(self.screen, (210,210,210), (5,(y+20)), (320, (y+20)), 1)
                self.screen.blit(textName, (5, y))
                y += 30

class AddPage(Page):
    def __init__(self, screen = pygame.display.set_mode((320, 480)), caption = pygame.display.set_caption("Contacts"), title = "Add Contact"):
        Page.__init__(self, screen, caption)
        self.title = title

    def style(self):
        Page.style(self)
        titleFont = pygame.font.SysFont("trebuchet ms", 38)
        textSurface = titleFont.render(self.title, True, (255,255,255))
        self.screen.blit(textSurface, (5, 18))
        AddButton().shape()
        CancelButton().shape()

class Button():
    def __init__(self, screen = pygame.display.set_mode((320, 480))):
        self.screen = screen

    def shape(self): 
        pygame.draw.rect(self.screen, (120,0,0), (270,12,40,40), 0)

class AddButton(Button):
    def __init__(self, screen = pygame.display.set_mode((320, 480))):
        Button.__init__(self, screen)

    def shape(self):
        Button.shape(self)
        pygame.draw.line(self.screen, (255,255,255), (289, 15), (289,48), 2)
        pygame.draw.line(self.screen, (255,255,255), (272, 31.5), (307, 31.5), 2)

class CancelButton(Button):
    def __init__(self, screen = pygame.display.set_mode((320, 480))):
        Button.__init__(self, screen)

    def shape(self):
        pygame.draw.rect(self.screen, (120,0,0), (245,20,25,25), 0)
        pygame.draw.aaline(self.screen, (255,255,255), (252,32.5), (263,26))
        pygame.draw.aaline(self.screen, (255,255,255), (252,32.5), (263,39))

pygame.init()
page = MainPage()
page.style()
page.printContacts()

while True:    
    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            sys.exit()
        elif event.type == KEYDOWN and event.key == K_ESCAPE:
            pygame.quit()
            sys.exit()  
        elif event.type == MOUSEBUTTONUP and event.button == 1 and isinstance(page, MainPage):
            if (pygame.mouse.get_pos() >= (270,13)) and (pygame.mouse.get_pos() >= (270,53)) and (pygame.mouse.get_pos() <= (309,13)) and (pygame.mouse.get_pos() <= (309,53)):
                page = AddPage()
                page.style()
        elif event.type == MOUSEBUTTONUP and event.button == 1 and isinstance(page, AddPage):
            if (pygame.mouse.get_pos() >= (270,13)) and (pygame.mouse.get_pos() >= (270,53)) and (pygame.mouse.get_pos() <= (309,13)) and (pygame.mouse.get_pos() <= (309,53)):
                page = MainPage()
                page.style()
                page.printContacts()
            elif (pygame.mouse.get_pos() >= (245,20)) and (pygame.mouse.get_pos() >= (245,45)) and (pygame.mouse.get_pos() <= (370,20)) and (pygame.mouse.get_pos() <= (370,45)):
                page = MainPage()
                page.style()
                page.printContacts()

    pygame.display.update()

    addressBook = AddressBook()
    addressBook.contactsList
    addressBook.contactsList.sort(key = lambda c: (c.lastName, c.firstName) if c.lastName else (c.firstName, ""))

    print("-------------------\nContacts\n")
    for i in addressBook.contactsList:
        print(i.firstName, i.lastName)
    print()

    print("Menu:\n\n1. Add Contact\n2. Show Contact Details\n3. Edit Contact\n4. Delete Contact\n5. Search Contact\n-------------------\n")

    choice = input()
    print()

    if choice == "1":
        #add contacts

        contact = AddressBook().addContact()

    elif choice == "2":
        contact = AddressBook().contactInfo()

    elif choice == "4":
        contact = AddressBook().delContact()

    elif choice == "5":
        contact = AddressBook().contactSearch()

    else:
        continue

我为混合使用pygame代码和原始python代码感到抱歉,因为我还没有完全把shell代码转成pygame能用的代码。所以联系人条目等还是通过Shell/控制台来完成的。我想先完成主屏幕,然后再继续实现通过pygame输入的功能。

1 个回答

10

正如furas在评论中提到的,使用像Tkinter或PyQt这样的图形用户界面(GUI)框架会更好,特别是如果你继续使用pygame,文本输入会让你非常头疼。虽然pygame有一些小部件库,但在我看来,它们都比不上真正的GUI框架。

不过,有一种简单的方法可以为你的应用程序添加滚动功能。你可以不直接把内容画到屏幕上,而是先把它画到一个比屏幕更高的中间Surface上。如果你想向下滚动,只需把这个中间Surface放在屏幕Surface的“上面”。

下面是一个使用鼠标滚轮滚动的简单示例:

import pygame
import string

pygame.init()

screen = pygame.display.set_mode((300, 300))
intermediate = pygame.surface.Surface((300, 600))
i_a = intermediate.get_rect()
x1 = i_a[0]
x2 = x1 + i_a[2]
a, b = (255, 0, 0), (60, 255, 120)
y1 = i_a[1]
y2 = y1 + i_a[3]
h = y2-y1
rate = (float((b[0]-a[0])/h),
         (float(b[1]-a[1])/h),
         (float(b[2]-a[2])/h)
         )
for line in range(y1,y2):
     color = (min(max(a[0]+(rate[0]*line),0),255),
              min(max(a[1]+(rate[1]*line),0),255),
              min(max(a[2]+(rate[2]*line),0),255)
              )
     pygame.draw.line(intermediate, color, (x1, line),(x2, line))

y = 20
f = pygame.font.SysFont('', 17)
for l in string.ascii_uppercase:
    intermediate.blit(f.render(l, True, (255, 255, 255)), (10, y))
    y += 20

clock = pygame.time.Clock()    
quit = False

scroll_y = 0

while not quit:
    quit = pygame.event.get(pygame.QUIT)
    for e in pygame.event.get():
        if e.type == pygame.MOUSEBUTTONDOWN:
            if e.button == 4: scroll_y = min(scroll_y + 15, 0)
            if e.button == 5: scroll_y = max(scroll_y - 15, -300)

    screen.blit(intermediate, (0, scroll_y))
    pygame.display.flip()
    clock.tick(60)

在这里输入图片描述

撰写回答