Zugriff auf Widget aus Gridlayout - Problem

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Benutzeravatar
Felix92
User
Beiträge: 133
Registriert: Mittwoch 7. November 2018, 17:57

Huhu,

und zwar habe ich 2 Probleme:

1. wie kann ich die Abstände der Buttons im Grid verändern ?
// setSpacing und setHorizontal / Vertical .. habe ich schon probiert
2. wie kann ich auf einzelne Buttons aus dem Grid zugreifen ?

#todo wie auf buttons im Grid zugreifen
#todo wenn button aus grid gedrückt setze diesen / x-1 / x+1 /y-1 /y+1 auf black
#todo wenn button schon schwarz setze ihn auf weiß

Ich muss dazu sagen ich habe noch nie mit PyQt gearbeitet da ich Anwendungen bisher immer mit Java(FX) geschrieben habe.
Ich wäre für jede Hilfe dankbar falls es im bisherigen Code Fehler bzw. unsauberen Code gibt wäre ich für jeden Hinweis ebenfalls dankbar !

MfG Felix

Hier mein bisheriger Code:

Code: Alles auswählen

import sys
import subprocess
from PyQt5.QtWidgets import (QWidget, QGridLayout, QPushButton, QApplication, )


class basicWindow(QWidget, QGridLayout):

    def __init__(self):
        super().__init__()
        self.setWindowTitle('Game')

        button1 = QPushButton("3 x 3", self)
        button1.move(95, 30)
        button1.setStyleSheet("background-color: yellow")
        button1.clicked.connect(self.button1clicked)

        button2 = QPushButton("4 x 4", self)
        button2.move(245, 30)
        button2.setStyleSheet("background-color: yellow")
        button2.clicked.connect(self.button2clicked)

        button3 = QPushButton("Restart", self)
        button3.move(650, 30)
        button3.setStyleSheet("background-color: yellow")
        button3.clicked.connect(self.button3clicked)

    def button1clicked(self):
        grid_layout = QGridLayout()
        self.setLayout(grid_layout)
        for x in range(3):
            for y in range(3):
                button = QPushButton()
                button.setFixedSize(80, 80)
                button.setStyleSheet("background-color: white")
                grid_layout.addWidget(button, x, y)
        button.clicked.connect(self.printcolor)

    def button2clicked(self):
        grid_layout = QGridLayout()
        self.setLayout(grid_layout)
        for x in range(4):
            for y in range(4):
                button = QPushButton()
                button.setFixedSize(80, 80)
                button.setStyleSheet("background-color: white")
                grid_layout.addWidget(button, x, y)

    def printcolor(self, button):
        button.setStyleSheet("background-color: black")
        #todo wie auf buttons im Grid zugreifen
        #todo wenn button aus grid gedrückt setze diesen / x-1 / x+1 /y-1 /y+1 auf black
        #todo wenn button schon schwarz setze ihn auf weiß



    def button3clicked(self):
        self.close()
        subprocess.call("python" + " game.py", shell=True)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = basicWindow()
    window.setStyleSheet("background-color: green")
    window.resize(800, 800)
    window.show()
    sys.exit(app.exec_())
Bild
__deets__
User
Beiträge: 14529
Registriert: Mittwoch 14. Oktober 2015, 14:29

Na merk dir doch die Buttons einfach. In einer geeigneten Datenstruktur. Nach welchem Kriterium willst du denn auf die zugreifen?
Benutzeravatar
__blackjack__
User
Beiträge: 13079
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Felix92: Erst mal ist es komisch das Deine Klasse von `QGridLayout` erbt. Das macht keinen Sinn.

Dann sollte man keine Widgets mit `move()` absolut platzieren sondern auch dafür Layoutklassen verwenden. Eventuell mit zusätzlichen Widgets als Container.

Auf die Buttons kannst Du zugreifen in dem Du Dir die Objekte in irgendeiner Datenstruktur merkst. Zum Beispiel eine verschachtelte Liste, die die 2D-Struktur wiederspiegelt, oder ein Wörterbuch das Tupel mit den Koordinaten auf die Button-Objekte abbildet.

Die Implementierung für den "Restart"-Button ist so gar keine gute Idee. Versuche das sauber innerhalb des gleichen Programms zu lösen statt das Programm immer wieder aus sich selbst heraus als neues *zusätzliches* Programm zu starten.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Benutzeravatar
Felix92
User
Beiträge: 133
Registriert: Mittwoch 7. November 2018, 17:57

Ok danke erstmal für eure Antworten allerdings muss ich sagen verstehe ich den Zugriff auf einzelne Elemente im Grid nicht so ganz.

Mein Gedanke war:
(Das ganze als Eventhandler)

wenn button aus grid geklickt dann:
Setze farbe für (button, x, y) auf schwarz
// selbes für die Buttons daneben drüber und drunter
Wenn button schwarz dann setze farbe auf weiß

Geht das in python nicht das ich direkt z.B. auf den Button an position(0, 0) zugreife ?
Und dann sage if button(0, 0) clicked set...
Weil die Elemente müssten nach dem erzeugen ja eigentlich alle vorhanden sein !?

Und für den Abstand hatte ich probiert:
grid_layout.setSpacing(0) allerdings gibt's dort nur ein paar hinter die Ohren 😂
__deets__
User
Beiträge: 14529
Registriert: Mittwoch 14. Oktober 2015, 14:29

Jein. Natürlich geht das: http://doc.qt.io/qt-5/qgridlayout.html#itemAtPosition

Ist halt nur ne doofe Idee. Denn wenn du jetzt feststellst, das du aus Layout-Gründen deinen Button nochmal in ein anderes Widget wickeln musst, dann bleibt der Code nach unserer Methode stabil. Aber nach deiner musst du lauter stellen anfassen. Darum macht man das so nicht.

Mit dem Layout kann ich dir nur empfehlen mit dem Designer rumzuspielen, bis es klappt.
Benutzeravatar
__blackjack__
User
Beiträge: 13079
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Felix92: Ich habe mir mal eben spasseshalber JavaFX (8) angeschaut, und da *muss* man sich das ja parallel als Datenstruktur vorhalten. Oder übersehe ich da etwas?
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Benutzeravatar
__blackjack__
User
Beiträge: 13079
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Ich habe das mal spasseshalber mit Jython und JavaFX implementiert:

Code: Alles auswählen

#!/usr/bin/env jython
# coding: utf-8
from __future__ import absolute_import, division, print_function
import sys
from functools import partial

from javafx.application import Application
from javafx.scene import Scene
from javafx.scene import control
from javafx.scene.layout import TilePane


class FXTest(Application):
    
    SIZE = 3
    BACKGROUND_COLOR_BLACK = '-fx-background-color: black;'
    BACKGROUND_COLOR_WHITE = '-fx-background-color: white;'
    STYLE_TO_INVERTED_STYLE = {
        BACKGROUND_COLOR_BLACK: BACKGROUND_COLOR_WHITE,
        BACKGROUND_COLOR_WHITE: BACKGROUND_COLOR_BLACK,
    }
    
    def __init__(self):
        self.cells = [[None] * self.SIZE for _ in xrange(self.SIZE)]
    
    def start(self, stage):
        pane = TilePane(prefColumns=self.SIZE)
        for i in xrange(self.SIZE):
            for j in xrange(self.SIZE):
                button = control.Button(
                    str(i * self.SIZE + j),
                    onAction=lambda event, y=i, x=j: self.invert_cross(x, y),
                    style=self.BACKGROUND_COLOR_WHITE,
                )
                pane.children.add(button)
                self.cells[i][j] = button
        stage.scene = Scene(pane)
        stage.show()

    def invert(self, x, y):
        if 0 <= x < self.SIZE and 0 <= y < self.SIZE:
            cell = self.cells[y][x]
            cell.style = self.STYLE_TO_INVERTED_STYLE[cell.style]

    def invert_cross(self, x, y):
        self.invert(x, y)
        self.invert(x - 1, y)
        self.invert(x + 1, y)
        self.invert(x, y - 1)
        self.invert(x, y + 1)


def main():
    FXTest.launch(FXTest().class, sys.argv)


if __name__ == '__main__':
    main()
Ich wüsste nicht wie man bei JavaFX (sinnvoll) darum herum kommen würde sich das wie hier in `self.cells` in einer 2D-Datenstruktur zu merken. Denn von dem `TilePane` kann man nur eine flache Liste mit den angezeigten `Node`-Objekten geben lassen.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Benutzeravatar
Felix92
User
Beiträge: 133
Registriert: Mittwoch 7. November 2018, 17:57

@_blackjack_ ich hätte das in Java (FX) mit dem SceneBuilder umgesetzt wäre meiner Meinung nach die schnellste und sauberste Angehensweise den Buttons eine/mehrere ID"s" zuweisen und die Methode/n für die Buttons schreiben ID implementieren fertig, da man somit den ganzen GUI Code gleich noch in fxml auslagert wäre in Python vermutlich auch einfacher als ich es anstelle allerdings habe ich dort noch nicht so viel Erfahrung :roll: .

Ok ein wenig bin ich weiter gekommen hätte dann gleich mal noch 2 Fragen .D
Und zwar habe ich probiert das die Farbe der Buttons nur geändert wird solange Sie sich im Grid befinden und alles andere ignoriert wird allerdings klappt das nicht so wie ich dachte führt immer zum Error da ich Buttons die nicht vorhanden sind bzw. ausserhalb des Grids liegen würden "anspreche" wie könnte ich das lösen !?
Über if/else habe ich es leider nicht hinbekommen also wenn loc[0] <0 do: pass else setBackgroundColor....etc.
Und 2tens wäre wie kann ich in Python die Farbe von Buttons vergleichen ?

Danke für eure Hilfe
MfG Felix

Hier der Code:

Code: Alles auswählen

import sys
import subprocess

from PyQt5.QtWidgets import (QWidget, QGridLayout, QPushButton, QApplication, QSizePolicy,)


class basicWindow(QWidget):

    def __init__(self):
        super().__init__()
        self.setWindowTitle('Game')

        button1 = QPushButton("3 x 3", self)
        button1.move(106, 115)
        button1.resize(38, 30)
        button1.setStyleSheet("background-color: grey")
        button1.clicked.connect(self.button1clicked)

        button2 = QPushButton("4 x 4", self)
        button2.move(153, 115)
        button2.resize(38, 30)
        button2.setStyleSheet("background-color: grey")
        button2.clicked.connect(self.button2clicked)

    def button1clicked(self):
        grid_layout = QGridLayout()
        self.setLayout(grid_layout)
        for x in range(3):
            for y in range(3):
                button = QPushButton()
                policy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
                button.setSizePolicy(policy)
                button.setStyleSheet("background-color: white")
                grid_layout.addWidget(button, x, y)
                button.clicked.connect(self.buttonColor)
        button3 = QPushButton("Restart", self)
        grid_layout.addChildWidget(button3)
        button3.move(125, 0)
        button3.resize(50, 20)
        button3.setStyleSheet("background-color: grey")
        button3.clicked.connect(self.button3clicked)

    def button2clicked(self):
        grid_layout = QGridLayout()
        self.setLayout(grid_layout)
        for x in range(4):
            for y in range(4):
                button = QPushButton()
                policy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
                button.setSizePolicy(policy)
                button.setStyleSheet("background-color: white")
                grid_layout.addWidget(button, x, y)
                button.clicked.connect(self.buttonColor)
        button3 = QPushButton("Restart", self)
        grid_layout.addChildWidget(button3)
        button3.move(125, 0)
        button3.resize(50, 20)
        button3.setStyleSheet("background-color: grey")
        button3.clicked.connect(self.button3clicked)

    def buttonColor(self):
        button = self.sender()
        index = self.layout().indexOf(button)
        loc = self.layout().getItemPosition(index)
        b1 = self.layout().itemAtPosition(loc[0], loc[1]).widget()
        b1.setStyleSheet("background-color: black")
        b2 = self.layout().itemAtPosition(loc[0] + 1, loc[1]).widget()
        b3 = self.layout().itemAtPosition(loc[0], loc[1] - 1).widget()
        b4 = self.layout().itemAtPosition(loc[0] - 1, loc[1]).widget()
        b5 = self.layout().itemAtPosition(loc[0], loc[1] + 1).widget()

        if loc[0] < 0:
            pass
        else:
            b4.setStyleSheet("background-color: black")

        if loc[0] > 2:
            pass
        else:
            b2.setStyleSheet("background-color: black")

        if loc[1] < 0:
            pass
        else:
            b3.setStyleSheet("background-color: black")

        if loc[1] > 2:
            pass
        else:
            b5.setStyleSheet("background-color: black")

    #print(index, loc[0], loc[1])

     #todo wenn button aus grid gedrückt setze diesen / x-1 / x+1 /y-1 /y+1 auf black
     #todo wenn button schon schwarz setze ihn auf weiß

    def button3clicked(self):
        self.close()
        subprocess.call("python" + " game.py", shell=True)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = basicWindow()
    window.setStyleSheet("background-color: orange")
    window.setFixedSize(300, 300)
    window.show()
    sys.exit(app.exec_())
Benutzeravatar
Felix92
User
Beiträge: 133
Registriert: Mittwoch 7. November 2018, 17:57

Der Code worum es geht:

Code: Alles auswählen

def buttonColor(self):
        button = self.sender()
        index = self.layout().indexOf(button)
        loc = self.layout().getItemPosition(index)
        b1 = self.layout().itemAtPosition(loc[0], loc[1]).widget()
        b1.setStyleSheet("background-color: black")
        b2 = self.layout().itemAtPosition(loc[0] + 1, loc[1]).widget()
        b3 = self.layout().itemAtPosition(loc[0], loc[1] - 1).widget()
        b4 = self.layout().itemAtPosition(loc[0] - 1, loc[1]).widget()
        b5 = self.layout().itemAtPosition(loc[0], loc[1] + 1).widget()

        if loc[0] < 0:
            pass
        else:
            b4.setStyleSheet("background-color: black")

        if loc[0] > 2:
            pass
        else:
            b2.setStyleSheet("background-color: black")

        if loc[1] < 0:
            pass
        else:
            b3.setStyleSheet("background-color: black")

        if loc[1] > 2:
            pass
        else:
            b5.setStyleSheet("background-color: black")

    #print(index, loc[0], loc[1])

     #todo wenn button aus grid gedrückt setze diesen / x-1 / x+1 /y-1 /y+1 auf black
     #todo wenn button schon schwarz setze ihn auf weiß
Benutzeravatar
__blackjack__
User
Beiträge: 13079
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Felix92: Wie gesagt: Ich würde eine eigene Datenstruktur erstellen statt das aus der GUI wieder irgendwie raus zu popeln. Und ich würde auch zusätzlich die Programmlogik noch unabhängig von der GUI machen. Du veranstaltest da viel zu viel in der GUI statt das die einfach nur zum anzeigen und entgegennehmen von Benutzerinteraktion ist, speicherst Du Daten in der GUI. Dafür ist die nicht gedacht.

Wenn man einen ``if``- oder einen ``else``-Zweig hat in dem nur ein ``pass`` steht, dann macht man offensichtlich etwas falsch, denn so etwas kann man *immer* vermeiden.

Wie man testet ob Koordinaten innerhalb bestimmter Grenzen liegen, habe ich in meinem Code zu stehen. Ganz einfach ``if`` und Vergleiche.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Antworten