I'm trying to build a chessboard consisting of buttons.
I created 3 widgets in one line. There are labels outside (filling) and inside I want to put a chessboard.
I would like it to always occupy 90% of the screen width and automatically adjust its height so that it always remains a square. It would also be necessary to set the buttons always to be squares but I also can't handle it. Can You help me?
class ChessBoard(GridLayout):
def __init__(self, **kwargs):
super(ChessBoard, self).__init__(**kwargs)
self.cols = 8
for i in range(64):
self.cell = Button(text="", size_hint_y=self.height/8, height=self.width/8)
self.add_widget(self.cell)
class ChessBoardContainer(GridLayout):
def __init__(self, **kwargs):
super(ChessBoardContainer, self).__init__(**kwargs)
self.orientation='horizontal'
self.cols=3
self.lab1 = Label(text="1")
self.add_widget(self.lab1)
self.board = ChessBoard()
self.add_widget(self.board)
self.lab2 = Label(text="2")
self.add_widget(self.lab2)
class CombWidget(BoxLayout):
def __init__(self, **kwargs):
super(CombWidget, self).__init__(**kwargs)
self.orientation='vertical'
self.but1 = Button(text="But1", font_size=40)
self.add_widget(self.but1)
self.chessb = ChessBoardContainer()
self.add_widget(self.chessb)
self.but2 = Button(text="But2", font_size=40)
self.add_widget(self.but2)
class MyPaintApp(App):
def build(self):
return CombWidget()
Right now this is my result:
I would like to get something like this (Paint master ;) ). Maybe it could be done without this labels?
To make buttons be squares you just have to set the height and width of GridLayout cells, and you are trying to do it with size_hint. Try this:
from kivy.core.window import Window
class ChessBoard(GridLayout):
def __init__(self, **kwargs):
super(ChessBoard, self).__init__(**kwargs)
self.cols = 8
winsize = Window.size
sizedict = {}
# to set width and height of GridLayout cells, you should make a dict, where the key is col's/row's number and the value is size
for i in range(self.cols):
sizedict[i] = winsize[0]/8 #or you can divide it by 10 for example to have some black filling on the sides
# and then simply do this
self.cols_minimum = sizedict
self.rows_minimum = sizedict
This code produces buttons that look fairly square to me. If you plan to use images for your chess pieces, the buttons will conform to the size of those.
from tkinter import Tk, Button
window = Tk ()
squares = []
index = 0
for x in range (8) :
for y in range (8) :
squares.append (Button (window, width = 7, height = 4))
squares [index].grid (row = x, column = y)
index += 1
window.mainloop ()
Related
I'm working on a GridLayout (here called CostosInput) that adds TextInputs to itself when a button is pressed. However, when it does, the layout doesn't resize the widgets around it, which causes it to overlap once a certain amount of TextInput widgets are added. How can I fix that?
It is part of another GridLayout, which has one column and is contained in a ScrollView. The widget below it (the one it overlaps with) is a Label.
I've tried looking in the Kivy docs, but the only stuff I've found is related to the size of Layouts and Widgets, and not necessarily their placement. I thought it'd be something like the layout_hint_with_bounds(sh_sum, available_space, min_bounded_size, sh_min_vals, sh_max_vals, hint) (link to the Kivy docs), but that's an internal function. I apologize if something doesn't conform to convention.
Here's the code for the CostoInput class.
class CostosInput(GridLayout):
def __init__(self, *args, **kwargs):
super(CostosInput, self).__init__()
self.cols = 2
self.spacing = 5
self.padding = 5
self.size_hint_y = 0.2
# used in add_lines()
self.count = 1
add = Orange_Button(text='AGREGAR COSTO')
add.bind(on_press=self.add_lines)
rem = Orange_Button(text='QUITAR COSTO')
rem.bind(on_press=self.del_lines)
self.add_widget(add)
self.add_widget(rem)
self.add_widget(Orange_Label(text='concepto'))
self.add_widget(Orange_Label(text='costo'))
self.add_lines()
# this function is adds TextInputs
# when the add button is pressed
def add_lines(self, *args, **kwargs):
text = Normal_TI()
text.text = 'costo ' + str(self.count)
text.bind(text=self.change)
flot = FloatInput()
flot.bind(text=self.change)
self.add_widget(text)
self.add_widget(flot)
self.size_hint_y += 0.1
self.count += 1
# this has nothing to do with the ui
def del_lines(self, *args, **kwargs):
for x in range(0, len(self.children) - 4, 2):
if len(self.children) > 4:
try:
if self.children[x].focus or self.children[x + 1].focus:
self.remove_widget(self.children[x])
self.remove_widget(self.children[x])
except Exception as err:
print('del lines: no se pudieron quitar,'
'err {}'.format(err))
# this also has nothing to do with the ui
def change(self, value, *args):
dicto = dict()
for x in range(0, len(self.children) - 4, 2):
dicto[self.children[x + 1].text] = self.children[x].text
print(dicto)
producto.costos = dicto
Here's the code for the class the label is instantiated with. The text attribute is the only part that changes.
class Orange_Label(Label):
def __init__(self, *args, **kwargs):
super(Orange_Label, self).__init__(*args, **kwargs)
self.size_hint_y = None
self.size = (self.width, 10)
self.color = (0.95668627451, 0.6941176471, 0.5137254902, 1)
When I run Python script in python3 it works well but in python 2.7 I get an error and I should run this code in Python 2.7 :
this is my code:I want to test some algorithms on path-finding and want to create a window consisting of grids where I can drag my mouse while clicked to create walls and delete them with right click and drag
import Tkinter
class Cell():
FILLED_COLOR_BG = "green"
EMPTY_COLOR_BG = "white"
FILLED_COLOR_BORDER = "green"
EMPTY_COLOR_BORDER = "black"
def __init__(self, master, x, y, size):
""" Constructor of the object called by Cell(...) """
self.master = master
self.abs = x
self.ord = y
self.size= size
self.fill= False
def _switch(self):
""" Switch if the cell is filled or not. """
self.fill= not self.fill
def draw(self):
""" order to the cell to draw its representation on the canvas """
if self.master != None :
fill = Cell.FILLED_COLOR_BG
outline = Cell.FILLED_COLOR_BORDER
if not self.fill:
fill = Cell.EMPTY_COLOR_BG
outline = Cell.EMPTY_COLOR_BORDER
xmin = self.abs * self.size
xmax = xmin + self.size
ymin = self.ord * self.size
ymax = ymin + self.size
self.master.create_rectangle(xmin, ymin, xmax, ymax, fill = fill, outline = outline)
class CellGrid(Canvas):
def __init__(self,master, rowNumber, columnNumber, cellSize, *args, **kwargs):
Canvas.__init__(self, master, width = cellSize * columnNumber , height = cellSize * rowNumber, *args, **kwargs)
self.cellSize = cellSize
self.grid = []
for row in range(rowNumber):
line = []
for column in range(columnNumber):
line.append(Cell(self, column, row, cellSize))
self.grid.append(line)
#memorize the cells that have been modified to avoid many switching of state during mouse motion.
self.switched = []
#bind click action
self.bind("<Button-1>", self.handleMouseClick)
#bind moving while clicking
self.bind("<B1-Motion>", self.handleMouseMotion)
#bind release button action - clear the memory of midified cells.
self.bind("<ButtonRelease-1>", lambda event: self.switched.clear())
self.draw()
def draw(self):
for row in self.grid:
for cell in row:
cell.draw()
def _eventCoords(self, event):
row = int(event.y / self.cellSize)
column = int(event.x / self.cellSize)
return row, column
def handleMouseClick(self, event):
row, column = self._eventCoords(event)
cell = self.grid[row][column]
cell._switch()
cell.draw()
#add the cell to the list of cell switched during the click
self.switched.append(cell)
def handleMouseMotion(self, event):
row, column = self._eventCoords(event)
cell = self.grid[row][column]
if cell not in self.switched:
cell._switch()
cell.draw()
self.switched.append(cell)
if __name__ == "__main__" :
app = Tk()
grid = CellGrid(app, 50, 50, 10)
grid.pack()
app.mainloop()
And error:
NameError: name 'Canvas' is not defined
How can I change my code in order to runnig this in Python 2.7?
Well, it's not defined all right, I wonder how could it run on Py3, unless Tkinter there dumped Canvas into your namespace somehow.
You probably wanted to do class CellGrid(Tkinter.Canvas), but I don't remember the exact structure of Tkinter offhand, so it might be in a subpackage or something.
You need to import elements of module you want to use, in your case:
from Tkinter import Canvas, Tk
You can also do:
from Tkinter import *
but it is not recomended way of doing. Your code may not work right, if you use many modules, and they two of them happend to have same name of method.
Last way is just
import Tkinker
but then every time you use element of this module, you need to add full path so if you wanna use Canvas, you need to useTkinker.Canvas.
Also remember that in Python 2.7 you import Tkinter and in 3.X import tkinter
I am trying to create a simple gui that displays the (memory) layout of some components of a device, but I am having a really hard time enforcing the policy I want to the displayed area.
Let me first show what I have so far (my code became quite large, but I changed/narrowed the code down to the minimal required for anyone to be able to run it):
#!/usr/local/bin/python3
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import sys
class Register(QGraphicsRectItem):
RegisterSize = 125
NameColor = QColor(Qt.blue)
ValueColor = QColor(0, 154, 205)
def __init__(self, name, value, pos, parent = None):
super(Register, self).__init__(parent)
self.setPos(pos)
self.width = Register.RegisterSize
self.height = 0
self.set_register_name(name)
self.set_register_value(value)
self.setRect(0, 0, self.width, self.height)
def set_register_name(self, name):
self.text_item = QGraphicsTextItem(name, self)
self.text_item.setDefaultTextColor(Register.NameColor)
self.height += self.text_item.boundingRect().height()
def set_register_value(self, value):
self.value_item = QGraphicsTextItem(str(value), self)
self.value_item.setDefaultTextColor(Register.ValueColor)
self.value_item.setPos(self.text_item.boundingRect().bottomLeft())
self.height += self.value_item.boundingRect().height()
class Title(QGraphicsTextItem):
TitleFont = 'Times New Roman'
TitleFontSize = 18
TitleColor = QColor(Qt.red)
def __init__(self, title, parent = None):
super(Title, self).__init__(title, parent)
self.setFont(QFont(Title.TitleFont, Title.TitleFontSize))
self.setDefaultTextColor(Title.TitleColor)
class Component(QGraphicsItem):
def __init__(self, parent = None):
super(Component, self).__init__(parent)
self.width = Register.RegisterSize * 4
self.height = 0
self.add_title()
self.add_registers()
self.rect = QRectF(0, 0, self.width, self.height)
def add_title(self):
self.title = Title('Component Layout', self)
self.title.setPos((self.width - self.title.boundingRect().width()) / 2, 0)
self.height += self.title.boundingRect().height()
def add_registers(self):
y_coor = self.height
x_coor = 0
for i in range(64):
register = Register('register {0:d}'.format(i), i, QPointF(x_coor, y_coor), self)
x_coor = ((i + 1) % 4) * Register.RegisterSize
if (i + 1) % 4 == 0:
y_coor += register.rect().height()
self.height = y_coor
def boundingRect(self):
return self.rect.adjusted(-1, -1, 1, 1)
def paint(self, painter, option, widget):
pen = QPen(Qt.blue)
painter.setPen(pen)
painter.drawRect(self.rect)
class Device(QGraphicsItem):
LeftMargin = 50
RightMargin = 50
TopMargin = 20
BottomMargin = 20
def __init__(self, parent = None):
super(Device, self).__init__(parent)
self.width = Device.LeftMargin + Device.RightMargin
self.height = Device.TopMargin + Device.BottomMargin
component = Component(self)
component.setPos(QPointF(Device.LeftMargin, Device.TopMargin))
self.width += component.boundingRect().width()
self.height += component.boundingRect().height()
self.rect = QRectF(0, 0, self.width, self.height)
def paint(self, painter, option, widget):
pass
def boundingRect(self):
return self.rect.adjusted(-1, -1, 1, 1)
class MainForm(QDialog):
def __init__(self, parent = None):
super(MainForm, self).__init__(parent)
self.scene = QGraphicsScene(parent)
self.view = QGraphicsView(self)
self.view.setScene(self.scene)
self.scene.addItem(Device())
self.resize(700, 900)
def run_app():
app = QApplication(sys.argv)
form = MainForm()
form.show()
app.exec_()
if __name__ == '__main__':
run_app()
This code, when launched, displays the following:
I don't mind the vertical scrollbar, since I intend to add more Components to the Device, and they won't all fit, what bothers me is the horizontal scrollbar.
Why does it appear without me explicitly asking?
It's not like there's no room in the window for the QGraphicsView to display the content.
Moreover, I noticed that the horizontal (and vertical) scrollbars do not appear, when only the Component is added to the QGraphicsView:
self.scene.addItem(Component()) # << previously was self.scene.addItem(Device())
Now the scrollbars do not appear:
Also; when I instead change the following lines:
LeftMargin = 0 # previously was 50
RightMargin = 0 # previously was 50
TopMargin = 0 # previously was 20
BottomMargin = 0 # previously was 20
Scrollbars do not appear. (I probably crossed some boundary with these margins added?)
I know I can control the scrollbars policy with the QGraphicsView.setHorizontalScrollBarPolicy() to make the horizontal scrollbar always off, but that raises another problem: When there's no way to scroll right, the vertical scrollbar "steals" some of the pixels from the display, making Device.RightMargin != Device.LeftMargin. Also, I am curious about what's the size boundary above which the horizontal/vertical scrollbars appear.
So, this is the policy I want to enforce:
I want the displayed area to always have a minimum height of X pixels (regardless of Device()'s height), and for vertical scrollbar to appear only if the Device() height passes these X pixels boundary (I'll determine Device()'s height by summing all Component()s heights)
I want QGraphicsView to never show horizontal scrollbar (the width Device()'s width is fixed and independent of the number of Component()s).
Whenever vertical scrollbar is needed, I don't want it to take up pixels from my display area.
I want to know what is the boundary (in pixels) above which scrollbars will appear (when I don't specify scrollbar policy).
EDIT:
After playing with it a bit, I figured something:
The unwanted horizontal scroll bar appears only because the vertical one appears and steals some of the display space.
According to the doc, the default policy for the horizontal scroll bar is Qt::ScrollBarAsNeeded, which means: "shows a scroll bar when the content is too large to fit and not otherwise.", but it doesn't state what is considered "too large".
When I played around with the margins (Device.TopMargin/Device.BottomMargin), I discovered that the vertical scroll bar appears (and consequently the horizontal one) when Device.boundingRect().height() crosses the 786 pixels boundary.
I couldn't figure out where did this number came from or how to control it.
I believe you are looking for setFixedWidth() and setFixedHeight()
class MainForm(QDialog):
def __init__(self, parent = None):
super(MainForm, self).__init__(parent)
self.scene = QGraphicsScene(parent)
self.view = QGraphicsView(self)
self.view.setScene(self.scene)
self.scene.addItem(Device())
self.resize(700, 900)
self.view.setFixedWidth(650) # <-
self.view.setFixedHeight(500) # <- these two lines will set Device dimensions
self.setFixedWidth(700) # <- this will fix window width
When you set fixed width to view it must be greater than its content (left margin + Device + right margin), otherwise horizontal scroll bar will be displayed. This is why you did not get the horizontal scroll bar when margins were zero.
Generally, the scrollbar will appear when your current view can't display the content.
The vertical scroll bar will take some space from inside the window, and I believe that you do not have control over that, so you should reserve some place for that, too. The behavior of vertical scroll bar depends on your windows system, e.g. on Mac it hovers over and disappear when unneeded, so it does not takes space at all.
I recommend to do the layout in QT Designer. I find it much easier to do it visually, testing it immediately and only introduce small changes in the generated code.
I want to get the value of a scale and create rectangles as many as the value is. For example, if I adjust the scale to number 7, 7 rectangles would be created next to each other, and after that if I adjust the scale value to 3, the rectangles shown in the canvas decreases to three at that moment. I had used the code below:
from tkinter import *
from tkinter import ttk
class rect:
def __init__(self, root):
self.root = root
self.size = IntVar()
self.canvas = Canvas(self.root, width=800, height=300)
self.scale = Scale(self.root, orient=HORIZONTAL, from_=3, to=20, tickinterval=1, variable=self.size)
self.show()
def show(self):
x = 50
y = 50
for i in range(self.scale.get()):
self.canvas.create_rectangle(x, y, x + 50, y + 50, fill='red')
x += 50
self.canvas.pack()
self.scale.pack()
root = Tk()
a = rect(root)
root.mainloop()
I guess I have to use trace method, But I don't know how to.
Can anyone fix the code I used in the way which I explained.
Thank you.
One solution is to bind to <ButtonRelease>, and call your show method there. Since event bindings pass an object representing the event, you'll need to make that an optional argument if you also want to call show without any arguments.
For example:
class rect:
def __init__(self, root):
...
self.scale = Scale(...)
self.scale.bind("<ButtonRelease>", self.show)
I'm guessing you'll want to remove any previously drawn rectangles, so you'll need to call delete before creating the rectangles:
def show(...):
self.canvas.delete("all")
...
I want to create a random rectangle on my rectangle class.
I have a function def randomRects(x,y,width,height): (x, y) is the top left corner and width must be less than 90, height less than 70.
I understand to create a random rectangle you have to do something like canvas.create_rectangle(x1,x2,y1,y1), but I'm not sure how to do it in this situation. I'm also not sure if width and height should be in parameters or not.
import random
class Rectangle:
def __init__(self, height, width):
self.height = 80
self.width = 100
#not sure if above is correct
def randomRects(x,y,width, height):
w = random.randrange(100)
h = random.randrange(80)
w.create_rectangle(x,y,width,height)
h.create_rectangle(x,y,width,height)
One way to do it is this:
#!/usr/bin/env python
from tkinter import *
import random
root = Tk()
class Recta:
def __init__(self, height, width):
self.height=80
self.width=100
def randomRects(self,canvas):
w = random.randrange(100)
h = random.randrange(80)
canvas.create_rectangle(0,0,h,w,fill='green')
c = Canvas(root)
c.pack()
tes = Recta(10,20)
tes.randomRects(c)
root.mainloop()
Of course the init method is a bit stupid, as it takes arguments but doesn't use them. Your code for that method was correct, though.