class Gui(QMainWindow): #class variables #Distance_right_limit = 50 Distance_right_limit = Config.distance_right_limit Level_number = Config.level_number #signal for emitting key presses key_pressed = pyqtSignal(QEvent) #if thegame ends these signals are raised lose_screen_signal = pyqtSignal() win_screen_signal = pyqtSignal() reset_signal = pyqtSignal() def __init__(self, gamefield): super(Gui, self).__init__() #calls the upper class init self.update_class_variables() #variables used to capture key press self.space = 0 self.right_arrow = 0 self.left_arrow = 0 self.enter = 0 self.count = 0 #mostly for testing use self.color = 0 self.game = False #tells if game is running self.scene = None #graphicsscene added later self.editor_scene = None self.gamefield = gamefield #gamefield object, added later self.gamefield_level = '' #tells which level (file) is currently used as gamefield #for capturing key presses self.key_pressed.connect(self.determine_key) #Main window size: #starting screen can be dynamically adjusted #for dynamic window size changes self.win_width = 0 self.win_width_prev = 0 self.win_height = 0 self.win_changed = False self.restart = False # is game is restarted the scene has to be adjusted self.screen = 0 #these are used to determine which object the player should see self.screen_prev = 0 self.setWindowTitle('Tasohyppely') #can be changed later #for the different screens self.first_win_screen = True self.first_lose_screen = True self.first_editor = True self.selecting_level = False self.editing_level = False self.end_text = QGraphicsTextItem('GAME OVER,') self.continue_text = QGraphicsTextItem( 'Continue by pressing the button') self.win_text = QGraphicsTextItem('YOU WON!') self.lose_text = QGraphicsTextItem('YOU LOST') self.message_box = QMessageBox( ) #set a handle to a message box which is used to display user usefull information #set layout to draw multiple objects self.layout = QHBoxLayout() #set a central widget and layout for it self.setCentralWidget(QWidget()) self.centralWidget().setLayout(self.layout) #connect all the end game signals self.__connect_signals__() #initialize all the buttons self.create_buttons() #handles to scene items, set when items are added to scene self.enemy_items = [] self.static_items = [] self.player_item = None #when game is started gui updates screen by calling drawgame self.timer = QTimer() self.timer.timeout.connect(self.update_game) #set timer value to 19 ms, can be changed later (a prime number) self.timer.start(19) #when level editor is activated self.editor_timer = QTimer() self.editor_timer.timeout.connect(self.level_editor) self.editor_timer.start( 31 ) #timer value (a prime number -> connections shouldn't happen same time) #background color self.setStyleSheet("""background: rgb(20,5,40);""") self.setAttribute(Qt.WA_NoSystemBackground, True) self.setAttribute(Qt.WA_TranslucentBackground, True) #self.select_level() #self.cancel_level_select() #self.set_level() self.game_() #self.cancel_game() GameField.Left_arrow += 1 GameField.Space += 1 self.start_game() #self.reset_gui() #self.start_game #self.start_level_editor() #self.save_level = True #self.level_editor() #self.cancel_level_editor() #self.help_screen() def create_buttons(self): #This is a helper method which creates buttons # Called from init #create a continue button for win and lose screens self.continue_button = QPushButton('Continue') self.continue_button.setFixedSize(0, 0) self.continue_button.clicked.connect(self.reset_gui) self.continue_button.setEnabled(False) #add continue button to the layout self.layout.addWidget(self.continue_button) #create a cancel button which moves user back to main menu from game screen self.cancel_game_button = QPushButton('Cancel') self.cancel_game_button.clicked.connect(self.cancel_game) self.cancel_game_button.setFixedSize(0, 0) self.cancel_game_button.setEnabled(False) self.cancel_game_button.setStyleSheet( 'QPushButton {background-color: transparent;color:red}' ) #can be changed later self.layout.addWidget(self.cancel_game_button) #create a cancel button which moves user back to main menu from level_select self.cancel_level = QPushButton('Cancel') self.cancel_level.clicked.connect(self.cancel_level_select) self.cancel_level.setFixedSize(0, 0) self.cancel_level.setEnabled(False) self.cancel_level.setStyleSheet( 'QPushButton {background-color: transparent;color:red}' ) #can be changed later self.layout.addWidget(self.cancel_level) #create a cancel button which moves user back to main menu from level editor self.cancel_editor = QPushButton('Cancel') self.cancel_editor.clicked.connect(self.cancel_level_editor) self.cancel_editor.setFixedSize(0, 0) self.cancel_editor.setEnabled(False) self.cancel_editor.setStyleSheet( 'QPushButton {background-color: transparent;color:red}' ) #can be changed later self.layout.addWidget(self.cancel_editor) #crete a button which leads to main game self.switch_button = QPushButton('Game') self.switch_button.clicked.connect(self.game_) self.switch_button.setFixedSize(100, 50) self.switch_button.setStyleSheet('QPushButton {color:green;}') self.layout.addWidget(self.switch_button) #create a button which leads to level select self.switch3 = QPushButton('Select level') self.switch3.clicked.connect(self.select_level) self.switch3.setFixedSize(100, 50) self.switch3.setStyleSheet('QPushButton {color:green;}') self.layout.addWidget(self.switch3) #create a button which leads to level editor self.switch4 = QPushButton('Level Editor') self.switch4.clicked.connect(self.start_level_editor) self.switch4.setFixedSize(100, 50) self.switch4.setStyleSheet('QPushButton {color:green;}') self.layout.addWidget(self.switch4) #create a button which opens a help screen self.switch2 = QPushButton('Help') self.switch2.clicked.connect(self.help_screen) self.switch2.setFixedSize(100, 50) self.switch2.setStyleSheet('QPushButton {color:green;}') self.layout.addWidget(self.switch2) #create a start button which starts the main game and connect to start game method self.start_button = QPushButton('Start Game') self.start_button.setStyleSheet( 'QPushButton {background-color: transparent;color:red}' ) #can be changed later self.start_button.clicked.connect(self.start_game) self.start_button.setFixedSize(0, 0) self.start_button.setEnabled(False) #add the start button to layout self.layout.addWidget(self.start_button) def help_screen(self): #This method is called after switch2 has been pressed #Switch2 opens a help dialog screen #This may not be the most sophisticated implementation self.help = QDialog() #set window title and add a vertical layout self.help.setWindowTitle('Help') self.help_layout = QVBoxLayout() self.help.setLayout(self.help_layout) #just add all help texts as labels label = QLabel() label.setText('GENERAL INFO:') self.help_layout.addWidget(label) label2 = QLabel() label2.setText( "'ESC' is used to exit the game\n" "'cancel'-button is always used to return to the mainmenu") self.help_layout.addWidget(label2) label3 = QLabel() label3.setText( "Press 'Game' in order to switch to a window where you can start the main game\n" "By pressing 'Select level' you can choose which level you want to play\n" "Press 'Level Editor' to create your own levels") self.help_layout.addWidget(label3) label4 = QLabel() label4.setText('CONTROLS:') self.help_layout.addWidget(label4) label5 = QLabel() label5.setText( "A (or left arrow) moves the player left\n" "D (or right arrow) moves the player right\n" "Space: jump upwards, Q: left and upwards, E: right and upwards") self.help_layout.addWidget(label5) label9 = QLabel() label9.setText('SELECT LEVEL') self.help_layout.addWidget(label9) label6 = QLabel() label6.setText( "To select a level type the level (file) name and press enter in 'Select level'" ) self.help_layout.addWidget(label6) label7 = QLabel() label7.setText("WINNING AND LOSING") self.help_layout.addWidget(label7) label8 = QLabel() label8.setText( "Reaching one win object (marked with a green color) leads to winning the game\n" "Jumping or falling on an enemy will destroy the enemy, otherwise touching an enemy will lead to losing the game\n" "Falling to a cave will lead losing the game") self.help_layout.addWidget(label8) #open the dialog self.help.open() def select_level(self): #set switches to the correct state self.set_switches_off('level') #initialize enter-key value and set correct state to selecting_level self.enter = 0 self.selecting_level = True self.level_text = '' #initialize a scene where to put levels self.level_scene = QGraphicsScene() #view for scene self.level_scene_view = QGraphicsView(self.level_scene, self) self.level_scene_view.adjustSize() self.level_scene_view.show() self.layout.addWidget(self.level_scene_view) level = QGraphicsTextItem('Level') path = os.getcwd() #get the current working dir #os.chdir(path) #change dir to the current working dir os.chdir('game_levels') position = 50 levels = [] data = os.listdir() for item in data: if os.path.isfile(item): levels.append(item) #add also item to level_scene to show user what levels there are to choose from #level = QGraphicsTextItem(item) level = QGraphicsTextItem(item[:-4]) #cut .txt ending #change color, can be changed later level.setDefaultTextColor(QColor(225, 100, 25)) self.level_scene.addItem(level) level.setPos(0, position) position += 20 self.qline_edit = QLineEdit() #change text color palette = QPalette() palette.setColor(QPalette.Text, QColor(225, 100, 25)) #the color can be changed self.qline_edit.setPalette(palette) self.layout.addWidget(self.qline_edit) self.qline_edit.textEdited[str].connect(self.qline_edited) os.chdir( path ) #remember to change back to the working dir where all classes etc are def qline_edited(self, text): #This is just a helper method which assigns qline edited text if self.editing_level: #it is called from level editor self.level_name = text else: #called from select level self.level_text = text + '.txt' #add .txt ending def set_level(self, reset): #This method sets the level to self.gamefield that user has chosen if possible #It is called via keypress event when enter is pressed and user is selecting level (self.selecting_level == True) #reset is a bool. if true this method is called from reset_gui otherwise this is called after keypress enter try: path = os.getcwd() #get the current working dir os.chdir(path) #change dir to the current working dir os.chdir('game_levels') #open the file if possible if not reset: file = open(self.level_text, 'r') else: #method is called from reset_gui file = open(self.gamefield_level, 'r') #parse a gamefield and a copy from the file gamefield = GameField() return_value = gamefield.parse_gamefield(file) file.close() if return_value: #if return value is False, the level is broken self.set_gamefield(gamefield) if not reset: #if method is called from reset_gui these shouldn't be executed self.gamefield_level = self.level_text #this is used by reset_gui method #show user a message screen that level change was successfull self.message_box.setIcon(QMessageBox.Information) self.message_box.setText('Level successfully changed') else: #create a message box that informs the user self.message_box.setIcon(QMessageBox.Warning) self.message_box.setText( 'Level is not ok, using the previous level') except FileNotFoundError: self.message_box.setIcon(QMessageBox.Warning) self.message_box.setText('Level with name {} not found'.format( self.level_text)) finally: os.chdir( path ) #This changes back to working directory where classes etc are defined if not reset: #show the message box to the user self.message_box.setWindowTitle('Level select') self.message_box.show() def cancel_level_select(self): #This method is used to switch back to the mainmenu view from level select self.selecting_level = False self.qline_edit.setFixedSize(0, 0) self.layout.removeWidget(self.qline_edit) #make the level_scene not visible self.level_scene.setSceneRect(0, 0, 0, 0) self.level_scene_view.setFixedSize(0, 0) #delete all the items in the scene self.level_scene.clear() self.layout.removeWidget( self.level_scene_view) #remove unnecessary scene view #also do style changes via self.cancel_game() def cancel_game(self): #This method returns the mainmenu view self.setWindowTitle('Tasohyppely') self.setCentralWidget(QWidget()) self.centralWidget().setLayout(self.layout) self.cancel_game_button.setFixedSize(0, 0) self.cancel_game_button.setEnabled(False) self.cancel_level.setFixedSize(0, 0) self.cancel_level.setEnabled(False) self.cancel_editor.setFixedSize(0, 0) self.cancel_editor.setEnabled(False) self.start_button.setFixedSize(0, 0) self.start_button.setEnabled(False) self.switch_button.setFixedSize(100, 50) self.switch_button.setEnabled(True) self.switch2.setFixedSize(100, 50) self.switch2.setEnabled(True) self.switch3.setFixedSize(100, 50) self.switch3.setEnabled(True) self.switch4.setEnabled(True) self.switch4.setFixedSize(100, 50) def set_switches_off(self, screen): #This is a helper method called to set starting screen switches off #sets also cancel_button on #screen tells if next screen is level_select or game self.switch_button.setFixedSize(0, 0) self.switch_button.setEnabled(False) self.switch2.setFixedSize(0, 0) self.switch2.setEnabled(False) self.switch3.setFixedSize(0, 0) self.switch3.setEnabled(False) self.switch4.setFixedSize(0, 0) self.switch4.setEnabled(False) if screen == 'game': #next screen is the game screen so turn on/off correct cancel buttons self.cancel_game_button.setFixedSize(70, 50) self.cancel_game_button.setEnabled(True) self.cancel_level.setFixedSize(0, 0) self.cancel_level.setEnabled(False) self.cancel_editor.setFixedSize(0, 0) self.cancel_editor.setEnabled(False) elif screen == 'level': #next screen is level select self.cancel_level.setFixedSize(70, 50) self.cancel_level.setEnabled(True) self.cancel_game_button.setFixedSize(0, 0) self.cancel_game_button.setEnabled(False) self.cancel_editor.setFixedSize(0, 0) self.cancel_editor.setEnabled(False) else: #next screen is level editor self.cancel_level.setFixedSize(0, 0) self.cancel_level.setEnabled(False) self.cancel_game_button.setFixedSize(0, 0) self.cancel_game_button.setEnabled(False) self.cancel_editor.setFixedSize(70, 50) self.cancel_editor.setEnabled(True) def game_(self): #This method creates window for starting the main game level_name = self.gamefield_level #set the level name as window title (remove .txt ending) self.setWindowTitle(level_name[:-4]) #Set the starting screen switches off self.set_switches_off('game') self.start_button.setFixedSize(70, 50) self.start_button.setEnabled(True) #read pic (it min size can be set later) self.game_pixmap = QPixmap('screen_pic.png') #for reuse widget = QLabel() widget.setPixmap(self.game_pixmap) widget.setScaledContents(True) #makes dynamic scaling possible #set pic to back side of the screen self.setCentralWidget(widget) self.centralWidget().setLayout(self.layout) def start_level_editor(self): #This method is called when user enters level editor self.editing_level = True #set the buttons to correct state self.set_switches_off('editor') #create a new scene (if there is None) and a view if self.editor_scene == None: self.editor_scene = QGraphicsScene() self.editor_scene_view = QGraphicsView(self.editor_scene, self) self.editor_scene_view.show() self.layout.addWidget(self.editor_scene_view) self.editor_scene.setSceneRect(0, 0, self.win_width, self.win_height) self.editor_scene_view.adjustSize() if self.first_editor: #create new objects if first scene otherwise use old (so that user can continue editing) #create a new level editor object self.level_edit = LevelEditor() #create a 'current' object (see LevelEditor) self.editor_current = DrawLevelObject(self.level_edit.current) self.editor_scene.addItem(self.editor_current) self.editor_items = [ ] #this is used to store pointers to items that are added to the scene self.first_editor = False self.save_level = False #This is used to detect when user wants to save a level self.level_name = '' #This is used to store level name def level_editor(self): #This is a method which updates gui when user is in level_editor if self.editing_level: if self.win_changed: #user has changed window size or game has been restarted when the rect has to be readjusted self.editor_scene.setSceneRect(0, 0, self.win_width, self.win_height) if self.save_level: #user wants to save a level self.save_level = False self.create_save_dialog() else: #move objects to correct positions self.level_edit.update_all_positions() self.editor_current.move( ) #current is different than other objects so it updated separately (see LevelEditor) #update the other objects self.add_level_draw_objects() self.remove_level_draw_objects() def create_save_dialog(self): #This is a helper method used to create a QDialog screen for level_editor when user wants to save a level #create a new dialog where user can set level_name self.save_dialog = QDialog() self.save_dialog.setWindowTitle('Save level') #set dialog a static size (because scaling makes this screen look horrible) self.save_dialog.setFixedSize( 350, 200) #This can be changed, but these seem to be pretty good values self.save_dialog.show() #add a layout to the dialog self.dialog_layout = QVBoxLayout() self.save_dialog.setLayout(self.dialog_layout) #add help texts label = QLabel() label.setText( "Check your level is not missing a player/finish position") label2 = QLabel() label2.setText( "Give your level a unique name\n" "(if not unique, level will be saved with a standard name)") label3 = QLabel() label3.setText( "Save level by pressing 'Save level', exit without saving with 'ESC'" ) self.dialog_layout.addWidget(label) self.dialog_layout.addWidget(label2) self.dialog_layout.addWidget(label3) #add a qline editor to get level_name self.editor_qline = QLineEdit() self.editor_qline.textEdited[str].connect(self.qline_edited) self.dialog_layout.addWidget(self.editor_qline) #add a button which confirms level_name self.save_button = QPushButton('Save level') self.save_button.clicked.connect(self.save_editor_level) self.save_button.setFixedSize(100, 70) self.dialog_layout.addWidget(self.save_button) def save_editor_level(self): #This method is used to save a level if possible #level name is set in self.level_name by user input to editor_qline #close the dialog self.save_dialog.reject() #try to save the level return_val = self.level_edit.create_file( self.level_name) #see LevelEditor create_level print(return_val) #print(self.level_name) def add_level_draw_objects(self): #This method is used to add a drawlevelobject for all objects #in self.level_edit #if an object is already added to the scene it doesn't do anything #Notice: using separate list for all the object causes that there is many mostly identical lines in this method (not so good implementation) if self.level_edit.player: #check it's not None if not self.level_edit.player.is_added: drawobject = DrawLevelObject(self.level_edit.player) #add pointer to the object self.editor_items.append(drawobject) #add item to the scene self.editor_scene.addItem(drawobject) self.level_edit.player.added() #see LevelObject #add enemies for i in range(len(self.level_edit.enemies)): if not self.level_edit.enemies[i].is_added: drawobject = DrawLevelObject(self.level_edit.enemies[i]) #add pointer to the object self.editor_items.append(drawobject) #add item to the scene self.editor_scene.addItem(drawobject) self.level_edit.enemies[i].added() for i in range(0, len(self.level_edit.visible)): if not self.level_edit.visible[i].is_added: drawobject = DrawLevelObject(self.level_edit.visible[i]) #add pointer to the object self.editor_items.append(drawobject) #add item to the scene self.editor_scene.addItem(drawobject) self.level_edit.visible[i].added() for i in range(0, len(self.level_edit.finish)): if not self.level_edit.finish[i].is_added: drawobject = DrawLevelObject(self.level_edit.finish[i]) #add pointer to the object self.editor_items.append(drawobject) #add item to the scene self.editor_scene.addItem(drawobject) self.level_edit.finish[i].added() for i in range(0, len(self.level_edit.invisible)): if not self.level_edit.invisible[i].is_added: drawobject = DrawLevelObject(self.level_edit.invisible[i]) #add pointer to the object self.editor_items.append(drawobject) #add item to the scene self.editor_scene.addItem(drawobject) self.level_edit.invisible[i].added() def remove_level_draw_objects(self): #This method is used to remove all inactive leveleditor object items from editor_scene #print(len(self.editor_items)) for i in range(len(self.editor_items)): if (not self.editor_items[i].owner.is_active) and ( not self.editor_items[i].is_deleted): #get all the non active items and remove them from the scene self.editor_scene.removeItem(self.editor_items[i]) self.editor_items[i].is_deleted = True def cancel_level_editor(self): #This method is called when user exits level editor self.editing_level = False #reset the scene and view self.editor_scene.setSceneRect(0, 0, 0, 0) self.editor_scene_view.setFixedSize(0, 0) #delete all the items in the scene #self.editor_scene.clear() self.layout.removeWidget( self.editor_scene_view) #remove unnecessary scene view #also do style changes via self.cancel_game() def __connect_signals__(self): #This method is called from init to init all the signals called #after the game has ended #This method is called only once self.reset_signal.connect(self.reset_gui) self.lose_screen_signal.connect(self.lose_screen) self.win_screen_signal.connect(self.win_screen) def update_class_variables(self): #This method is called from init to update all the gui class variables to match Config Gui.Distance_right_limit = Config.distance_right_limit Gui.Level_number = Config.level_number def keyPressEvent(self, event): self.key_pressed.emit(event) # connects to determine_key -method def determine_key(self, event): #determines if pressed key should cause some actions #and forwards key press information #This method is used to capture key presses for all the 'subgames' so it's pretty messy if event.key() == Qt.Key_Return: self.enter += 1 if self.selecting_level: self.set_level(False) #see set_level method elif self.editing_level: #user wants to save a level self.save_level = True if event.key( ) == Qt.Key_Space: # this is called whenever the space is pressed self.space += 1 if self.game: GameField.Space += 1 #print(GameField.Space) #print (self.space) if event.key() == Qt.Key_A or event.key() == Qt.Key_Left: self.left_arrow += 1 if self.game: GameField.Left_arrow += 1 #print(GameField.Left_arrow) elif self.editing_level: LevelEditor.Left_arrow += 1 #print(self.left_arrow) if event.key() == Qt.Key_D or event.key() == Qt.Key_Right: self.right_arrow += 1 if self.game: GameField.Right_arrow += 1 #print(GameField.Right_arrow) elif self.editing_level: LevelEditor.Right_arrow += 1 #print(self.right_arrow) if event.key() == Qt.Key_Q: self.left_arrow += 1 #if Q is pressed, the player wants jump left self.space += 1 if self.game: GameField.Left_arrow += 1 GameField.Space += 1 if event.key( ) == Qt.Key_E: #if E is pressed, the player wants jump right self.right_arrow += 1 #update: in level editor user wants add an enemy self.space += 1 if self.game: GameField.Right_arrow += 1 GameField.Space += 1 elif self.editing_level: LevelEditor.Key_E += 1 elif event.key() == Qt.Key_Up or event.key() == Qt.Key_W: if self.editing_level: LevelEditor.Up_arrow += 1 elif event.key() == Qt.Key_Down or event.key() == Qt.Key_S: if self.editing_level: LevelEditor.Down_arrow += 1 elif event.key() == Qt.Key_P: if self.editing_level: LevelEditor.Key_P += 1 elif event.key() == Qt.Key_O: if self.editing_level: LevelEditor.Key_O += 1 elif event.key() == Qt.Key_F: if self.editing_level: LevelEditor.Key_F += 1 elif event.key() == Qt.Key_I: if self.editing_level: LevelEditor.Key_I += 1 elif event.key() == Qt.Key_Delete: if self.editing_level: LevelEditor.Delete += 1 elif event.key() == Qt.Key_Escape: print("Exit button activated") self.close() def resizeEvent(self, event): #used to adjust scene object positions #This redefines Qt5 resizeEvent self.win_width = event.size().width() self.win_height = event.size().height() self.win_changed = True def start_game(self): #change to the game window self.cancel_game_button.setEnabled(False) self.cancel_game_button.setFixedSize(0, 0) self.start_button.setEnabled(False) self.start_button.setFixedSize(0, 0) #set new central widget for drawing the game self.setCentralWidget(QWidget()) self.centralWidget().setLayout(self.layout) #init drawing scene for drawing game and add drawobjects to scene self.init_drawing_scene() self.add_DrawGame_objects() self.get_scene_items_number() #for testing only self.game = True GameField.Game_running = True def update_game(self): if self.game: #for testing only if self.count == 0: #self.setStyleSheet("""background: rgb(20,5,40);""") self.count += 1 if self.win_changed or self.restart: #user has changed window size or game has been restarted when the rect has to be readjusted self.scene.setSceneRect(0, 0, self.win_width, self.win_height) self.correct_scene_item_positions() self.win_changed = False self.restart = False self.gamefield.update_objects_positions() self.player_item.move_item() #see drawdynamic #also enemies has to be moved for i in range(len(self.enemy_items)): #print(self.enemy_items[i].owner_object) self.enemy_items[i].move_item() #see drawdynamic #now check where player is in the screen and adjust position based on that information self.adjust_moving_items_coordinates() #check if also static items have to be moved if self.screen != self.screen_prev: #static items have to be moved self.adjust_static_objects_positions() self.screen_prev = self.screen if not GameField.Game_running: #this must be reimplemented later #there should be separate screens for winning and losing the game if GameField.Fail: self.lose_screen_signal.emit() else: self.win_screen_signal.emit() #self.reset_signal.emit() def reset_gui(self): #this method is called when game has ended and is #switched back to starting screen #this is test version of this method #Init win and lose screen values for a new game self.first_win_screen = True self.first_lose_screen = True self.restart = True # This adjusts scene rect when user restarts the game #initialize gamefield's key press capturing class variables GameField.Right_arrow = 0 GameField.Left_arrow = 0 GameField.Space = 0 #create same widget as in init, make scene and view not drawn self.scene.setSceneRect(0, 0, 0, 0) self.scene_view.setFixedSize(0, 0) #delete all the items in the scene self.scene.clear() self.layout.removeWidget( self.scene_view) #remove unnecessary scene view #These need to be redefined because the old items were destroyed above self.end_text = QGraphicsTextItem('GAME OVER,') self.continue_text = QGraphicsTextItem( 'Continue by pressing the button') self.win_text = QGraphicsTextItem('YOU WON!') self.lose_text = QGraphicsTextItem('YOU LOST') #open the file and set level by calling set_level, it should always success because this file has been already opened and checked self.set_level(True) #initialize all lists of scene items self.enemy_items = [] self.static_items = [] GameField.Fail = False #set the correct screen style widget = QLabel() widget.setPixmap(self.game_pixmap) widget.setScaledContents(True) self.setCentralWidget(widget) self.centralWidget().setLayout(self.layout) #enable start and cancel buttons and disable continue button self.start_button.setEnabled(True) self.start_button.setFixedSize(70, 50) self.cancel_game_button.setEnabled(True) self.cancel_game_button.setFixedSize(70, 50) self.continue_button.setFixedSize(0, 0) self.continue_button.setEnabled(False) #this is for changing the background color, prior was used for testing (that's why name is not good) self.count = 0 def lose_screen(self): #This method is called if a player has lost the game #It creates a screen that informs the player that the game has been lost #After the player continues from this screen reset_gui method will be called if self.first_lose_screen: #stop the game self.game = False GameField.Game_running = False #Enable continue button self.continue_button.setStyleSheet('QPushButton {color : red;}') self.continue_button.setFixedSize(100, 100) self.continue_button.setEnabled(True) #Set correct texts in the scene self.end_text.setDefaultTextColor(QColor(250, 0, 0)) self.lose_text.setDefaultTextColor(QColor(250, 0, 0)) self.continue_text.setDefaultTextColor(QColor(90, 20, 20)) self.scene.addItem(self.end_text) self.scene.addItem(self.lose_text) self.scene.addItem(self.continue_text) self.first_win_screen = False self.end_text.setPos(self.win_width * 0.5 - 100, 30) self.lose_text.setPos(self.win_width * 0.5 - 100, 50) self.continue_text.setPos(self.win_width * 0.5 - 150, 100) def win_screen(self): #This method is called if a player has win the game #It creates a screen that informs the player that the game has been won #After the player continues from this screen reset_gui method will be called if self.first_win_screen: #stop the game self.game = False GameField.Game_running = False #Enable continue button self.continue_button.setStyleSheet('QPushButton {color : green;}') self.continue_button.setFixedSize(100, 100) self.continue_button.setEnabled(True) #Set correct texts in the scene self.end_text.setDefaultTextColor(QColor(0, 180, 0)) self.win_text.setDefaultTextColor(QColor(0, 180, 0)) self.continue_text.setDefaultTextColor(QColor(20, 90, 20)) self.scene.addItem(self.end_text) self.scene.addItem(self.win_text) self.scene.addItem(self.continue_text) self.first_win_screen = False self.end_text.setPos(self.win_width * 0.5 - 100, 30) self.win_text.setPos(self.win_width * 0.5 - 100, 50) self.continue_text.setPos(self.win_width * 0.5 - 150, 100) def init_drawing_scene(self): #initializes the scene where objects are placed self.scene = QGraphicsScene() #view for scene self.scene_view = QGraphicsView(self.scene, self) self.scene_view.adjustSize() #self.scene_view.show() self.layout.addWidget(self.scene_view) self.scene_view.show() def set_gamefield(self, gamefield): #helper method which sets handle to gamefield object self.gamefield = gamefield def add_DrawGame_objects(self): #this method requires that a gamefield has been set #this method requires that a drawing scene has been set #it adds correct drawgame type objects for whole gamefield content #this method is called only once per started game, otherwise it creates duplicates #create drawitem for player and add it to scene #first create pixmap from player model picture and scale it pixmap = QPixmap('player_model_clear_3.png') pixmap = pixmap.scaled(Position.player_width, Position.player_heigth) player_item = DrawDynamic(self.gamefield.player, pixmap) self.scene.addItem(player_item) #add pointer to the item so that it's pos can be changed self.player_item = player_item self.player_item.move_item() #create drawitem for all enemies and add them to scene for i in range(len(self.gamefield.enemies)): #create pixmap and scale it corectly pixmap = QPixmap('enemy_model_clear1.png') pixmap = pixmap.scaled(Position.enemy_width, Position.enemy_heigth) enemy_item = DrawDynamic(self.gamefield.enemies[i], pixmap) self.scene.addItem(enemy_item) #move item to correct location enemy_item.move_item() #add enemy items to list so they can be moved self.enemy_items.append(enemy_item) #create drawitem for all ground objects for i in range(0, len(self.gamefield.ground_objects)): ground_item = DrawGround(self.gamefield.ground_objects[i]) self.scene.addItem(ground_item) #add to the list of static objects self.static_items.append(ground_item) #create drawitem for all finidsh and visible objects for i in range(0, len(self.gamefield.static_objects)): if self.gamefield.static_objects[i].type == 'visible_object': visible_item = DrawVisible(self.gamefield.static_objects[i]) self.scene.addItem(visible_item) self.static_items.append(visible_item) elif self.gamefield.static_objects[i].type == 'finish_object': finish_item = DrawFinish(self.gamefield.static_objects[i]) self.scene.addItem(finish_item) self.static_items.append(finish_item) def correct_scene_item_positions(self): #This method is called after user has changed window size #it sets new y_min coordinates for ground items in scene #so that window remains looking the same style as before adjusting the size delta_y = self.win_height - GameField.MainWindow_Heigth if delta_y > 0: #correct ground objects y-coordinates: for i in range(0, len(self.static_items)): if self.static_items[i].type == 'ground_object': x = self.static_items[i].x #left corner y = self.static_items[i].y #upper corner width = self.static_items[i].width height = self.static_items[i].height + delta_y self.static_items[ i].new_height = height # used by adjust_static_objects_positions #set new height self.static_items[i].setRect(x, y, width, height) def adjust_moving_items_coordinates(self): #This method is called after player and enemy objects are moved #It adjust x_min coordinates to match player position #value = self.player_item.owner_object.position.x_max // (self.win_width -Gui.Distance_right_limit) #value = sel if self.player_item.owner_object.position.x_max >= ((self.win_width -Gui.Distance_right_limit) + \ self.screen * (self.win_width - Gui.Distance_right_limit - Position.Distance_x)): self.screen += 1 #player is on the right edge outside of the current screen self.win_width_prev = self.win_width #this is done cause user can ajust screen anytime #and we don't want dynamic objects to move when the aren't near the edges elif (self.screen != 0) and self.player_item.owner_object.position.x_max < ((self.win_width -Gui.Distance_right_limit) + \ (self.screen -1) * (self.win_width - Gui.Distance_right_limit - Position.Distance_x)): self.screen -= 1 #player is on the left edge of the current screen self.win_width_prev = self.win_width if self.screen > 0: delta_x = self.screen * ( self.win_width_prev - Position.Distance_x - Gui.Distance_right_limit ) #Distance_x tells where the most left object should be located in the screen # adjust player item location y = self.player_item.owner_object.position.y_max #upper corner x = self.player_item.owner_object.position.x_min #left corner self.player_item.setPos(x - delta_x, y) # adjust enemy positions for i in range(len(self.enemy_items)): y = self.enemy_items[i].owner_object.position.y_max x = self.enemy_items[i].owner_object.position.x_min self.enemy_items[i].setPos(x - delta_x, y) #if value = 0, player is still in screen and nothing has to be done #notice that self.screen is also used to adjust static object locations def adjust_static_objects_positions(self): #this method is called when self.screen != self.screen_prev # it adjusts static_object position to correctly fit object to the screen delta_x = self.screen * (self.win_width - Position.Distance_x - Gui.Distance_right_limit) for i in range(len(self.static_items)): y = self.static_items[i].y #upper corner, it's not changed x = self.static_items[i].x - delta_x #left corner width = self.static_items[i].width height = self.static_items[i].height if self.static_items[i].type == 'ground_object': if self.static_items[i].new_height != 0: #if correct_scene_item_positions method has been called ground's draw height doesn't match owner_object height height = self.static_items[i].new_height self.static_items[i].setRect(x, y, width, height) def get_scene_items_number(self): #this is for testing use only print('there is {} items in scene'.format(len(self.scene.items())))
class Window(QMainWindow): ''' The class GUI handles the drawing of the main gui and allows user to interact with it. ''' def __init__(self, expenses, income, level_amount): super(Window, self).__init__() self.setCentralWidget(QWidget()) self.vertical = QVBoxLayout() self.horizontal = QHBoxLayout() self.centralWidget().setLayout(self.vertical) self.setWindowTitle("PyQtChart Line") self.setGeometry(100, 100, 1000, 1000) #self.setCentralWidget(linediag) self.show() data_list = [ [ "/home/markus/repos/azure-accounting/src/data/main_database_2019.json", 2019 ], [ "/home/markus/repos/azure-accounting/src/data/main_database.json", 2020 ] ] data_path = "/home/markus/repos/azure-accounting/src/data/main_database.json" self.draw_block_diagram(data_path) self.draw_method_pie(data_path) self.draw_cumulative_line(data_list) self.vertical.addWidget(self.block_diagram) self.horizontal.addWidget(self.linediag) self.horizontal.addWidget(self.piediag) self.vertical.addLayout(self.horizontal) self.show() def draw_method_pie(self, data_path): pie_qseries_2020 = PaymentMethods(data_path) self.piediag = PieDiagram( pie_qseries_2020, "empty", "Cumulative yearly expenses").create_piechart() def draw_cumulative_line(self, data_list): line_qseries_list = [] x_axis = QCategoryAxis() for row in data_list: line_qseries_list.append( YearLineAnalytics(row[0]).construct_cumulative_qseries(row[1])) self.linediag = LineDiagram( line_qseries_list, x_axis, "Cumulative yearly expenses").create_linechart() self.linediag.setFixedSize(500, 500) def draw_block_diagram(self, data_path): testdata = [{ "dy": 433, "dx": 327.7153558052434, "x": 0, "y": 0 }, { "dy": 330.0862676056338, "dx": 372.2846441947566, "x": 327.7153558052434, "y": 0 }, { "dy": 102.9137323943662, "dx": 215.0977944236371, "x": 327.7153558052434, "y": 330.0862676056338 }, { "dy": 102.9137323943662, "dx": 68.94160077680677, "x": 542.8131502288805, "y": 330.0862676056338 }, { "dy": 80.40135343309854, "dx": 88.24524899431273, "x": 611.7547510056874, "y": 330.0862676056338 }, { "dy": 22.51237896126767, "dx": 88.2452489943124, "x": 611.7547510056874, "y": 410.4876210387323 }] # Add a view for showing the scene self.block_diagram = QGraphicsView( BlockDiagram(testdata, [], "Test").add_tree_items("income"), self) self.block_diagram.adjustSize()
class Gui(QMainWindow): #class variables #Distance_right_limit = 50 Distance_right_limit = Config.distance_right_limit Level_number = Config.level_number #signal for emitting key presses key_pressed = pyqtSignal(QEvent) #if thegame ends these signals are raised lose_screen_signal = pyqtSignal() win_screen_signal = pyqtSignal() reset_signal = pyqtSignal() def __init__(self): super(Gui, self).__init__() #calls the upper class init self.update_class_variables() #variables used to capture key press self.space = 0 self.right_arrow = 0 self.left_arrow = 0 self.enter = 0 self.count = 0 #mostly for testing use self.color = 0 self.game = False #tells if game is running self.scene = None #graphicsscene added later self.gamefield = None #gamefield object, added later self.gamefield_level = '' #tells which level (file) is currently used as gamefield #for capturing key presses self.key_pressed.connect(self.determine_key) #Main window size: #starting screen can be dynamically adjusted #for dynamic window size changes self.win_width = 0 self.win_width_prev = 0 self.win_height = 0 self.win_changed = False self.restart = False # is game is restarted the scene has to be adjusted self.screen = 0 #these are used to determine which object the player should see self.screen_prev = 0 self.setWindowTitle('Tasohyppely') #can be changed later #for the different screens self.first_win_screen = True self.first_lose_screen = True self.selecting_level = False self.end_text = QGraphicsTextItem('GAME OVER,') self.continue_text = QGraphicsTextItem( 'Continue by pressing the button') self.win_text = QGraphicsTextItem('YOU WON!') self.lose_text = QGraphicsTextItem('YOU LOST') #set layout to draw multiple objects self.layout = QHBoxLayout() #set a central widget and layout for it self.setCentralWidget(QWidget()) self.centralWidget().setLayout(self.layout) #connect all the end game signals self.__connect_signals__() #initialize all the buttons self.create_buttons() #handles to scene items, set when items are added to scene self.enemy_items = [] self.static_items = [] self.player_item = None #when game is started gui updates screen by calling drawgame self.timer = QTimer() self.timer.timeout.connect(self.update_game) #set timer value to 20 ms, can be changed later self.timer.start(20) #self.select_level() #self.cancel_level_select() #self.set_level() #self.start_game() #self.reset_gui() #self.start_game() def create_buttons(self): #This is a helper method which creates buttons # Called from init #create a continue button for win and lose screens self.continue_button = QPushButton('Continue') self.continue_button.setFixedSize(0, 0) self.continue_button.clicked.connect(self.reset_gui) self.continue_button.setEnabled(False) #add continue button to the layout self.layout.addWidget(self.continue_button) #create a cansel button which moves user back to main menu self.cancel_button = QPushButton('Cancel') self.cancel_button.clicked.connect(self.cancel_game) self.cancel_button.setFixedSize(0, 0) self.cancel_button.setEnabled(False) self.cancel_button.setStyleSheet( 'QPushButton {background-color: transparent;}' ) #can be changed later self.layout.addWidget(self.cancel_button) #crete a button which leads to main game self.switch_button = QPushButton('Game') self.switch_button.clicked.connect(self.game_) self.switch_button.setFixedSize(100, 50) self.layout.addWidget(self.switch_button) #create a button which leads to level select self.switch3 = QPushButton('Select level') self.switch3.clicked.connect(self.select_level) self.switch3.setFixedSize(100, 50) self.layout.addWidget(self.switch3) #create a button which leads to level editor self.switch4 = QPushButton('Level Editor') self.switch4.clicked.connect(self.level_editor) self.switch4.setFixedSize(100, 50) self.layout.addWidget(self.switch4) #create a button which opens a help screen self.switch2 = QPushButton('Help') self.switch2.clicked.connect(self.switch2_) self.switch2.setFixedSize(100, 50) self.layout.addWidget(self.switch2) #create a start button which starts the main game and connect to start game method self.start_button = QPushButton('Start Game') self.start_button.setStyleSheet( 'QPushButton {background-color: transparent;}' ) #can be changed later self.start_button.clicked.connect(self.start_game) self.start_button.setFixedSize(0, 0) self.start_button.setEnabled(False) #add the start button to layout self.layout.addWidget(self.start_button) def switch2_(self): print('moi') def select_level(self): #set switches to the correct state self.set_switches_off() self.cancel_button.clicked.connect(self.cancel_level_select) #initialize enter-key value and set correct state to selecting_level self.enter = 0 self.selecting_level = True self.level_text = '' #initialize a scene where to put levels self.level_scene = QGraphicsScene() #view for scene self.level_scene_view = QGraphicsView(self.level_scene, self) self.level_scene_view.adjustSize() self.level_scene_view.show() self.layout.addWidget(self.level_scene_view) level = QGraphicsTextItem('Level') path = os.getcwd() #get the current working dir os.chdir(path) #change dir to the current working dir os.chdir('game_levels') position = 50 levels = [] data = os.listdir() for item in data: if os.path.isfile(item): levels.append(item) #add also item to level_scene to show user what levels there are to choose from level = QGraphicsTextItem(item) self.level_scene.addItem(level) level.setPos(0, position) position += 20 self.qline_edit = QLineEdit() self.layout.addWidget(self.qline_edit) self.qline_edit.textEdited[str].connect(self.qline_edited) os.chdir( path ) #remember to change back to the working dir where all classes etc are def qline_edited(self, text): #This is just a helper method which assigns qline edited text self.level_text = text def set_level(self, reset): #This method sets the level to self.gamefield that user has chosen if possible #It is called via keypress event when enter is pressed and user is selecting level (self.selecting_level == True) #reset is a bool. if true this method is called from reset_gui otherwise this is called after keypress enter try: path = os.getcwd() #get the current working dir os.chdir(path) #change dir to the current working dir os.chdir('game_levels') #open the file if possible if not reset: file = open(self.level_text, 'r') else: #method is called from reset_gui file = open(self.gamefield_level, 'r') #parse a gamefield and a copy from the file gamefield = GameField() return_value = gamefield.parse_gamefield(file) file.close() if return_value: #if return value is False, the level is broken self.set_gamefield(gamefield) if not reset: #if method is called from reset_gui these shouldn't be executed self.gamefield_level = self.level_text #this is used by reset_gui method print('Level successfully changed') else: print('Level is not ok, using the previous level') except FileNotFoundError: print('Level with name {} not found'.format(self.level_text)) finally: os.chdir( path ) #This changes back to working directory where classes etc are defined def cancel_level_select(self): #This method is used to switch back to the mainmenu view from level select self.selecting_level = False self.qline_edit.setFixedSize(0, 0) #make the level_scene not visible self.level_scene.setSceneRect(0, 0, 0, 0) self.level_scene_view.setFixedSize(0, 0) #delete all the items in the scene self.level_scene.clear() self.cancel_button.clicked.connect(self.cancel_game) def cancel_game(self): #This method returns the mainmenu view self.setCentralWidget(QWidget()) self.centralWidget().setLayout(self.layout) self.cancel_button.setFixedSize(0, 0) self.cancel_button.setEnabled(False) self.start_button.setFixedSize(0, 0) self.start_button.setEnabled(False) self.switch_button.setFixedSize(100, 50) self.switch_button.setEnabled(True) self.switch2.setFixedSize(100, 50) self.switch2.setEnabled(True) self.switch3.setFixedSize(100, 50) self.switch3.setEnabled(True) self.switch4.setEnabled(True) self.switch4.setFixedSize(100, 50) def set_switches_off(self): #This is a helper method called to set starting screen switches off #sets also cancel_button on self.switch_button.setFixedSize(0, 0) self.switch_button.setEnabled(False) self.switch2.setFixedSize(0, 0) self.switch2.setEnabled(False) self.switch3.setFixedSize(0, 0) self.switch3.setEnabled(False) self.switch4.setFixedSize(0, 0) self.switch4.setEnabled(False) self.cancel_button.setFixedSize(70, 50) self.cancel_button.setEnabled(True) def game_(self): #Set the starting screen switches off self.set_switches_off() self.start_button.setFixedSize(70, 50) self.start_button.setEnabled(True) #read pic (it min size can be set later) pixmap = QPixmap( 'start_screen_test.png') #this pic will be changed later widget = QLabel() widget.setPixmap(pixmap) widget.setScaledContents(True) #makes dynamic scaling possible #set pic to back side of the screen self.setCentralWidget(widget) self.centralWidget().setLayout(self.layout) def level_editor(self): print('level editor') def __connect_signals__(self): #This method is called from init to init all the signals called #after the game has ended #This method is called only once self.reset_signal.connect(self.reset_gui) self.lose_screen_signal.connect(self.lose_screen) self.win_screen_signal.connect(self.win_screen) def update_class_variables(self): #This method is called from init to update all the gui class variables to match Config Gui.Distance_right_limit = Config.distance_right_limit Gui.Level_number = Config.level_number def keyPressEvent(self, event): self.key_pressed.emit(event) # connects to determine_key -method def determine_key(self, event): if event.key() == Qt.Key_Return: self.enter += 1 if self.selecting_level: self.set_level(False) #see set_level method if event.key( ) == Qt.Key_Space: # this is called whenever the space is pressed self.space += 1 if self.game: GameField.Space += 1 print(GameField.Space) #print (self.space) if event.key() == Qt.Key_A or event.key() == Qt.Key_Left: self.left_arrow += 1 if self.game: GameField.Left_arrow += 1 print(GameField.Left_arrow) #print(self.left_arrow) if event.key() == Qt.Key_D or event.key() == Qt.Key_Right: self.right_arrow += 1 if self.game: GameField.Right_arrow += 1 print(GameField.Right_arrow) #print(self.right_arrow) if event.key() == Qt.Key_Q: self.left_arrow += 1 #if Q is pressed, the player wants jump left self.space += 1 if self.game: GameField.Left_arrow += 1 GameField.Space += 1 if event.key( ) == Qt.Key_E: #if E is pressed, the player wants jump right self.right_arrow += 1 self.space += 1 if self.game: GameField.Right_arrow += 1 GameField.Space += 1 elif event.key() == Qt.Key_Escape: print("Exit button activated") self.close() def resizeEvent(self, event): #used to adjust scene object positions #This redefines Qt5 resizeEvent self.win_width = event.size().width() self.win_height = event.size().height() self.win_changed = True def start_game(self): #change to the game window self.cancel_button.setEnabled(False) self.cancel_button.setFixedSize(0, 0) self.start_button.setEnabled(False) self.start_button.setFixedSize(0, 0) #set new central widget for drawing the game self.setCentralWidget(QWidget()) self.centralWidget().setLayout(self.layout) #init drawing scene for drawing game and add drawobjects to scene self.init_drawing_scene() self.add_DrawGame_objects() self.get_scene_items_number() #for testing only self.game = True GameField.Game_running = True def update_game(self): if self.game: #for testing only if self.count == 0: self.setStyleSheet("""background: rgb(20,5,40);""") self.count += 1 if self.win_changed or self.restart: #user has changed window size or game has been restarted when rect has to be readjusted self.scene.setSceneRect(0, 0, self.win_width, self.win_height) self.correct_scene_item_positions() self.win_changed = False self.restart = False self.gamefield.update_objects_positions() self.player_item.move_item() #see drawdynamic #also enemies has to be moved for i in range(len(self.enemy_items)): #print(self.enemy_items[i].owner_object) self.enemy_items[i].move_item() #see drawdynamic #now check where player is in the screen and adjust position based on that information self.adjust_moving_items_coordinates() #check if also static items have to be moved if self.screen != self.screen_prev: #static items have to be moved self.adjust_static_objects_positions() self.screen_prev = self.screen if not GameField.Game_running: #this must be reimplemented later #there should be separate screens for winning and losing the game if GameField.Fail: self.lose_screen_signal.emit() else: self.win_screen_signal.emit() #self.reset_signal.emit() def reset_gui(self): #this method is called when game has ended and is #switched back to starting screen #this is test version of this method #Init win and lose screen values for a new game self.first_win_screen = True self.first_lose_screen = True self.restart = True # This adjusts scene rect when user restarts the game #initialize gamefield's key press capturing class variables GameField.Right_arrow = 0 GameField.Left_arrow = 0 GameField.Space = 0 #create same widget as in init, make scene and view not drawn self.scene.setSceneRect(0, 0, 0, 0) self.scene_view.setFixedSize(0, 0) #delete all the items in the scene self.scene.clear() #These need to be redefined because the old items were destroyed above self.end_text = QGraphicsTextItem('GAME OVER,') self.continue_text = QGraphicsTextItem( 'Continue by pressing the button') self.win_text = QGraphicsTextItem('YOU WON!') self.lose_text = QGraphicsTextItem('YOU LOST') #open the file and set level by calling set_level, it should always success because this file has been already opened and checked self.set_level(True) #initialize all lists of scene items self.enemy_items = [] self.static_items = [] #set the correct screen style GameField.Fail = False pixmap = QPixmap( 'start_screen_test.png') #this pic will be changed later widget = QLabel() widget.setPixmap(pixmap) widget.setScaledContents(True) self.setCentralWidget(widget) self.centralWidget().setLayout(self.layout) #enable start and cancel buttons and disable continue button self.start_button.setEnabled(True) self.start_button.setFixedSize(70, 50) self.cancel_button.setEnabled(True) self.cancel_button.setFixedSize(70, 50) self.continue_button.setFixedSize(0, 0) self.continue_button.setEnabled(False) #this is for changing the background color, prior was used for testing (that's why name is not good) self.count = 0 def lose_screen(self): #This method is called if a player has lost the game #It creates a screen that informs the player that the game has been lost #After the player continues from this screen reset_gui method will be called if self.first_lose_screen: #stop the game self.game = False GameField.Game_running = False #Enable continue button self.continue_button.setStyleSheet('QPushButton {color : red;}') self.continue_button.setFixedSize(100, 100) self.continue_button.setEnabled(True) #Set correct texts in the scene self.end_text.setDefaultTextColor(QColor(250, 0, 0)) self.lose_text.setDefaultTextColor(QColor(250, 0, 0)) self.continue_text.setDefaultTextColor(QColor(90, 20, 20)) self.scene.addItem(self.end_text) self.scene.addItem(self.lose_text) self.scene.addItem(self.continue_text) self.first_win_screen = False self.end_text.setPos(self.win_width * 0.5 - 100, 30) self.lose_text.setPos(self.win_width * 0.5 - 100, 50) self.continue_text.setPos(self.win_width * 0.5 - 150, 100) def win_screen(self): #This method is called if a player has win the game #It creates a screen that informs the player that the game has been won #After the player continues from this screen reset_gui method will be called if self.first_win_screen: #stop the game self.game = False GameField.Game_running = False #Enable continue button self.continue_button.setStyleSheet('QPushButton {color : green;}') self.continue_button.setFixedSize(100, 100) self.continue_button.setEnabled(True) #Set correct texts in the scene self.end_text.setDefaultTextColor(QColor(0, 180, 0)) self.win_text.setDefaultTextColor(QColor(0, 180, 0)) self.continue_text.setDefaultTextColor(QColor(20, 90, 20)) self.scene.addItem(self.end_text) self.scene.addItem(self.win_text) self.scene.addItem(self.continue_text) self.first_win_screen = False self.end_text.setPos(self.win_width * 0.5 - 100, 30) self.win_text.setPos(self.win_width * 0.5 - 100, 50) self.continue_text.setPos(self.win_width * 0.5 - 150, 100) def init_drawing_scene(self): #initializes the scene where objects are placed self.scene = QGraphicsScene() #view for scene self.scene_view = QGraphicsView(self.scene, self) self.scene_view.adjustSize() self.scene_view.show() self.layout.addWidget(self.scene_view) self.scene_view.show() def set_gamefield(self, gamefield): #helper method which sets handle to gamefield object self.gamefield = gamefield def add_DrawGame_objects(self): #this method requires that a gamefield has been set #this method requires that a drawing scene has been set #it adds correct drawgame type objects for whole gamefield content #this method is called only once per started game, otherwise it creates duplicates #create drawitem for player and add it to scene #first create pixmap from player model picture and scale it pixmap = QPixmap('player_model_clear_3.png') pixmap = pixmap.scaled(Position.player_width, Position.player_heigth) player_item = DrawDynamic(self.gamefield.player, pixmap) self.scene.addItem(player_item) #add pointer to the item so that it's pos can be changed self.player_item = player_item self.player_item.move_item() #create drawitem for all enemies and add them to scene for i in range(len(self.gamefield.enemies)): #create pixmap and scale it corectly pixmap = QPixmap('enemy_model_clear1.png') pixmap = pixmap.scaled(Position.enemy_width, Position.enemy_heigth) enemy_item = DrawDynamic(self.gamefield.enemies[i], pixmap) self.scene.addItem(enemy_item) #move item to correct location enemy_item.move_item() #add enemy items to list so they can be moved self.enemy_items.append(enemy_item) #create drawitem for all ground objects for i in range(0, len(self.gamefield.ground_objects)): ground_item = DrawGround(self.gamefield.ground_objects[i]) self.scene.addItem(ground_item) #add to the list of static objects self.static_items.append(ground_item) #create drawitem for all finidsh and visible objects for i in range(0, len(self.gamefield.static_objects)): if self.gamefield.static_objects[i].type == 'visible_object': visible_item = DrawVisible(self.gamefield.static_objects[i]) self.scene.addItem(visible_item) self.static_items.append(visible_item) elif self.gamefield.static_objects[i].type == 'finish_object': finish_item = DrawFinish(self.gamefield.static_objects[i]) self.scene.addItem(finish_item) self.static_items.append(finish_item) def correct_scene_item_positions(self): #This method is called after user has changed window size #it sets new y_min coordinates for ground items in scene #so that window remains looking the same style as before adjusting the size delta_y = self.win_height - GameField.MainWindow_Heigth if delta_y > 0: #correct ground objects y-coordinates: for i in range(0, len(self.static_items)): if self.static_items[i].type == 'ground_object': x = self.static_items[i].x #left corner y = self.static_items[i].y #upper corner width = self.static_items[i].width height = self.static_items[i].height + delta_y self.static_items[ i].new_height = height # used by adjust_static_objects_positions #set new height self.static_items[i].setRect(x, y, width, height) def adjust_moving_items_coordinates(self): #This method is called after player and enemy objects are moved #It adjust x_min coordinates to match player position #value = self.player_item.owner_object.position.x_max // (self.win_width -Gui.Distance_right_limit) #value = sel if self.player_item.owner_object.position.x_max >= ((self.win_width -Gui.Distance_right_limit) + \ self.screen * (self.win_width - Gui.Distance_right_limit - Position.Distance_x)): self.screen += 1 #player is on the right edge outside of the current screen self.win_width_prev = self.win_width #this is done cause user can ajust screen anytime #and we don't want dynamic objects to move when the aren't near the edges elif (self.screen != 0) and self.player_item.owner_object.position.x_max < ((self.win_width -Gui.Distance_right_limit) + \ (self.screen -1) * (self.win_width - Gui.Distance_right_limit - Position.Distance_x)): self.screen -= 1 #player is on the left edge of the current screen self.win_width_prev = self.win_width if self.screen > 0: delta_x = self.screen * ( self.win_width_prev - Position.Distance_x - Gui.Distance_right_limit ) #Distance_x tells where the most left object should be located in the screen # adjust player item location y = self.player_item.owner_object.position.y_max #upper corner x = self.player_item.owner_object.position.x_min #left corner self.player_item.setPos(x - delta_x, y) # adjust enemy positions for i in range(len(self.enemy_items)): y = self.enemy_items[i].owner_object.position.y_max x = self.enemy_items[i].owner_object.position.x_min self.enemy_items[i].setPos(x - delta_x, y) #if value = 0, player is still in screen and nothing has to be done #notice that self.screen is also used to adjust static object locations def adjust_static_objects_positions(self): #this method is called when self.screen != self.screen_prev # it adjusts static_object position to correctly fit object to the screen delta_x = self.screen * (self.win_width - Position.Distance_x - Gui.Distance_right_limit) for i in range(len(self.static_items)): y = self.static_items[i].y #upper corner, it's not changed x = self.static_items[i].x - delta_x #left corner width = self.static_items[i].width height = self.static_items[i].height if self.static_items[i].type == 'ground_object': if self.static_items[i].new_height != 0: #if correct_scene_item_positions method has been called ground's draw height doesn't match owner_object height height = self.static_items[i].new_height self.static_items[i].setRect(x, y, width, height) def get_scene_items_number(self): #this is for testing use only print('there is {} items in scene'.format(len(self.scene.items())))
class SimulationLayout(QWidget): def __init__(self, boids_number): super(SimulationLayout, self).__init__() self.boids_number = boids_number self.boids = [] # Lisataan haluttu maara boid-yksiloita listaan for i in range(self.boids_number): self.boids.append(Boid()) self.buildLayout() self.ws = DEFAULT_S self.wa = DEFAULT_A self.wc = DEFAULT_C def buildLayout(self): self.sld1 = WeightSlider('Separation', DEFAULT_S, RANGE_S) self.sld1.slider.valueChanged.connect( lambda: get_ws(self.sld1.slider.value())) self.sld2 = WeightSlider('Alignment', DEFAULT_A, RANGE_A) self.sld2.slider.valueChanged.connect( lambda: get_wa(self.sld2.slider.value())) self.sld3 = WeightSlider('Cohesion', DEFAULT_C, RANGE_C) self.sld3.slider.valueChanged.connect( lambda: get_wc(self.sld3.slider.value())) scene = self.setScene() self.view = QGraphicsView(scene) self.view.adjustSize() # Estetaan scroll barit self.view.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.view.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.vertical = QVBoxLayout() self.horizontal1 = QHBoxLayout() self.horizontal2 = QHBoxLayout() self.horizontal1.addWidget(self.view) self.vertical.addLayout(self.horizontal1) # Lisataan ikkunaan graphicsviewin alapuolelle kolme painokerroin-slideria self.horizontal2.addWidget(self.sld1) self.horizontal2.addWidget(self.sld2) self.horizontal2.addWidget(self.sld3) self.vertical.addWidget(QLabel('Adjust parameters')) self.vertical.addLayout(self.horizontal2) self.setLayout(self.vertical) def setScene(self): # Lisaa parven yksilot sceneen scene = QGraphicsScene() scene.setSceneRect(0, 0, SCENE_WIDTH, SCENE_HEIGHT) scene.addItem(QGraphicsRectItem(0, 0, SCENE_WIDTH, SCENE_HEIGHT)) for boid in self.boids: scene.addItem(boid) boid.setGraphics() boid.updatePosVector() return scene