TypeError: 'type' object is not subscriptable This is the script - tuples

File "c:\Users\danie\AppData\Local\Programs\PythonCodingPack\Lib\site-packages\microgen\shape\basicGeometry.py", line 26, in BasicGeometry
center: tuple[float, float, float] = (0, 0, 0),
TypeError: 'type' object is not subscriptable
This is the script
"""
Basic Geometry (:mod:microgen.shape.basicGeometry)
"""
import cadquery as cq
from typing import Union
from typing import Tuple
class BasicGeometry:
"""
BasicGeometry class to manage shapes
:param shape: name of the shape
:param center: center
:param orientation: orientation
"""
numInstances = 0
def __init__(
self,
shape: str,
center: tuple[float, float, float] = (0, 0, 0),
orientation: tuple[float, float, float] = (0, 0, 0),
) -> None:
self.number = self.numInstances
self.shape = shape
self.center = center
self.orientation = orientation
self.name = self.shape + "_" + str(self.number)
self.geometry = None # type: Union[cq.Shape, None]
BasicGeometry.numInstances += 1

Related

Undo\Redo with multi items movement QGraphicScene - pyqt5

I have implemented QUndoStack with QGraphicScene and everything is working like charm with individual QGraphicItem movement/rotation, but when it comes to the multi-items movement, I couldn't find any way to do so, since I didn't find any flag/signal that will be emitted after all selected item position has been changed [ NOT DURING].
Here is a sketch code that will represent my goals.
import sys
from typing import Dict
from PyQt5.QtCore import Qt, QPoint
from PyQt5.QtGui import QPen, QColor, QBrush
from PyQt5.QtWidgets import QApplication, QGraphicsItem, QMainWindow, QGraphicsScene, QGraphicsView, \
QGraphicsEllipseItem, QUndoStack, QUndoCommand
import random
class CustomUndoRedoStack(QUndoStack):
instance = None
def __init__(self):
if not CustomUndoRedoStack.instance:
CustomUndoRedoStack.instance = self
super(CustomUndoRedoStack, self).__init__()
#staticmethod
def get_instance():
return CustomUndoRedoStack.instance
class MovingMultiItemsCommand(QUndoCommand):
def __init__(self, new_items_pos: Dict[QGraphicsItem], old_items_pos: Dict[QGraphicsItem]):
super(MovingMultiItemsCommand, self).__init__()
self.new_items_pos = new_items_pos
self.old_items_pos = old_items_pos
CustomUndoRedoStack.get_instance().push(self)
def undo(self) -> None:
for item in self.old_items_pos:
item.setPos(self.old_items_pos[item])
super(MovingMultiItemsCommand, self).undo()
def redo(self) -> None:
for item in self.new_items_pos:
item.setPos(self.new_items_pos[item])
super(MovingMultiItemsCommand, self).redo()
class CustomQGraphicsEllipseItem(QGraphicsEllipseItem):
def __init__(self, x, y):
super(CustomQGraphicsEllipseItem, self).__init__(x, y, 20, 20)
self.setFlag(QGraphicsItem.ItemIsSelectable)
self.setFlag(QGraphicsItem.ItemIsMovable)
self.setPen(QPen(QColor(255, 128, 0), 0.5, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin))
self.setBrush(QBrush(QColor(255, 128, 20, 128)))
class MyGraphicsView(QGraphicsView):
def __init__(self):
super(MyGraphicsView, self).__init__()
self.setDragMode(QGraphicsView.RubberBandDrag)
self._isPanning = False
self._mousePressed = False
self.setCacheMode(QGraphicsView.CacheBackground)
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
class MyGraphicsScene(QGraphicsScene):
def __init__(self):
super(MyGraphicsScene, self).__init__()
self.setBackgroundBrush(QBrush(QColor(50, 50, 50)))
self.old_items_pos = {}
self.new_items_pos = {}
# ------- I want something like the following -----------------------------
def selected_items_before_change(self):
for item in self.selectedItems():
self.old_items_pos[item] = item.pos()
def selected_items_after_change(self):
for item in self.selectedItems():
self.new_items_pos[item] = item.pos()
def selected_item_position_changed(self):
"""
This method must be triggered only and only once after selected items position has been changed (NOT WHILE)
"""
MovingMultiItemsCommand(self.new_items_pos, self.old_items_pos)
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
class MyMainWindow(QMainWindow):
def __init__(self):
super(MyMainWindow, self).__init__()
self.setWindowTitle("Test")
self.resize(800, 600)
self.gv = MyGraphicsView()
self.gv.setScene(MyGraphicsScene())
self.setCentralWidget(self.gv)
self.populate()
def populate(self):
scene = self.gv.scene()
for i in range(500):
x = random.randint(0, 1000)
y = random.randint(0, 1000)
rect = CustomQGraphicsEllipseItem(x, y)
scene.addItem(rect)
def main():
app = QApplication(sys.argv)
ex = MyMainWindow()
ex.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
C++ answers will be good as well, but Python is preferable :)
The mouse movement of selectable items is handled by the graphics scene, starting with its mousePressEvent() and ending with the corresponding mouseReleaseEvent().
This means that you can create a QUndoCommand based on the selected items (if any) after the default implementation of mouse button press/release is called. This will be valid for any movement, including when just one item has been moved.
class ItemMovedUndoCommand(QUndoCommand):
def __init__(self, oldPositions, newPositions):
if len(oldPositions) == 1:
pos = oldPositions.values()[0]
text = 'Item moved to {}x{}'.format(
pos.x(), pos.y())
else:
text = '{} items moved'.format(len(oldPositions))
super().__init__(text)
self.oldPositions = oldPositions
self.newPositions = newPositions
def redo(self):
for item, pos in self.newPositions.items():
item.setPos(pos)
def undo(self):
for item, pos in self.oldPositions.items():
item.setPos(pos)
class CustomQGraphicsEllipseItem(QGraphicsEllipseItem):
def __init__(self, x, y):
super(CustomQGraphicsEllipseItem, self).__init__(-10, -10, 20, 20)
self.setPos(x, y)
self.setFlag(QGraphicsItem.ItemIsSelectable)
self.setFlag(QGraphicsItem.ItemIsMovable)
self.setPen(QPen(QColor(255, 128, 0),
0.5, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin))
self.setBrush(QBrush(QColor(255, 128, 20, 128)))
class MyGraphicsView(QGraphicsView):
def __init__(self):
super(MyGraphicsView, self).__init__()
self.setDragMode(QGraphicsView.RubberBandDrag)
self._isPanning = False
self._mousePressed = False
self.setCacheMode(QGraphicsView.CacheBackground)
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
class MyGraphicsScene(QGraphicsScene):
itemsMoved = pyqtSignal(object, object)
def __init__(self):
super(MyGraphicsScene, self).__init__()
self.setBackgroundBrush(QBrush(QColor(50, 50, 50)))
self.oldPositions = {}
def mousePressEvent(self, event):
super().mousePressEvent(event)
if event.button() == Qt.LeftButton:
self.oldPositions = {i:i.pos() for i in self.selectedItems()}
def mouseReleaseEvent(self, event):
super().mouseReleaseEvent(event)
if event.button() == Qt.LeftButton and self.oldPositions:
self.itemsMoved.emit(self.oldPositions,
{i:i.pos() for i in self.oldPositions.keys()})
self.oldPositions = {}
class MyMainWindow(QMainWindow):
def __init__(self):
super(MyMainWindow, self).__init__()
self.setWindowTitle("Test")
self.resize(800, 600)
self.gv = MyGraphicsView()
self.scene = MyGraphicsScene()
self.gv.setScene(self.scene)
self.setCentralWidget(self.gv)
toolBar = QToolBar()
self.addToolBar(Qt.TopToolBarArea, toolBar)
self.undoStack = QUndoStack()
toolBar.addAction(self.undoStack.createUndoAction(self.scene))
toolBar.addAction(self.undoStack.createRedoAction(self.scene))
self.populate()
self.scene.itemsMoved.connect(self.itemsMoved)
def itemsMoved(self, oldPositions, newPositions):
self.undoStack.push(ItemMovedUndoCommand(oldPositions, newPositions))
def populate(self):
scene = self.gv.scene()
for i in range(500):
x = random.randint(0, 1000)
y = random.randint(0, 1000)
rect = CustomQGraphicsEllipseItem(x, y)
scene.addItem(rect)

Python : How to release `enter` key in python

I am using python-2.7 and kivy.When i run test.py then shows a form with 2 TextInput.
I am moving from one TextInput to another TextInput using Enter key.When i do not fill both TextInput and move using enter key.After that i focused ok button and i press Enter key then i call insert function and check it is blank or not.It it's blank then i set focus on Name TextInput using this function.
def insert(self):
if self.name.text == "":
self.name.focus = True
After focus name TextInput it calls root.on_enter_text_input() by default.So it move on next TextInput.But i want focus on name TextInput.
Can someone tell me how to stop root.on_enter_text_input() function at this time when i call insert function?
[![enter image description here][1]][1]
test.py
from kivy.uix.screenmanager import Screen
from kivy.app import App
from kivy.core.window import Window
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import StringProperty, ObjectProperty
from kivy.clock import Clock
Window.clearcolor = (0.5, 0.5, 0.5, 1)
Window.size = (500, 300)
class User(Screen):
name = ObjectProperty(None)
class1 = ObjectProperty(None)
#cls = ObjectProperty(None)
def __init__(self, **kwargs):
super(User, self).__init__(**kwargs)
Window.bind(on_key_down=self._on_keyboard_down)
Clock.schedule_once(self.name_focus, 1)
def name_focus(self, *args):
self.name.focus = True
def on_enter_text_input(self):
self.class1.focus = True
def on_enter_text_input1(self):
self.postUser.focus = True
self.postUser.background_color = [0.5, 0.5, 0.5, 1]
def _on_keyboard_down(self, instance, keyboard, keycode, text, modifiers):
if (hasattr(self.postUser, 'focus') and self.postUser.focus) and keycode == 40: # 40 - Enter key pressed
self.postUser.focus = False
self.postUser.background_color = [0, 0, 1, 0.5]
self.insert()
def insert(self):
if self.name.text == "":
self.name.focus = True
class Test(App):
def build(self):
return self.root
if __name__ == '__main__':
Test().run()
test.kv
#:kivy 1.10.0
User:
name: name
class1:class1
postUser : postUser
BoxLayout:
orientation: "vertical"
GridLayout:
cols: 2
padding: 20, 20
spacing: 10, 10
Label:
text: "Name"
text_size: self.size
valign: 'middle'
TextInput:
id:name
multiline: False
text_size: self.size
on_text_validate: root.on_enter_text_input()
Label:
text: "Class"
text_size: self.size
valign: 'middle'
TextInput:
id:class1
multiline: False
text_size: self.size
on_text_validate: root.on_enter_text_input1()
GridLayout:
cols: 2
padding: 0, 0
spacing: 5, 0
size_hint: .5, .35
pos_hint: {'x': .25, 'y': 0}
Button:
id:postUser
size_hint_x: .5
text: "Ok"
focus: False
on_release:
root.insert()
root.dismiss()
The solution is to add return True indicating that we have consumed the Enter key pressed and don’t want it to propagate any further. Please refer to the snippets below.
Snippets
def _on_keyboard_down(self, instance, keyboard, keycode, text, modifiers):
if (hasattr(self.postUser, 'focus') and self.postUser.focus) and keycode == 40: # 40 - Enter key pressed
self.postUser.focus = False
self.postUser.background_color = [0, 0, 1, 0.5]
self.insert()
return True
Output
You could do it like below
class User(Screen):
name = ObjectProperty(None)
class1 = ObjectProperty(None)
#cls = ObjectProperty(None)
def __init__(self, **kwargs):
super(User, self).__init__(**kwargs)
self.initial_focusing = False
Window.bind(on_key_down=self._on_keyboard_down)
Clock.schedule_once(self.name_focus, 1)
def name_focus(self, *args):
self.name.focus = True
def on_enter_text_input(self):
if self.initial_focusing:
self.initial_focusing = False
else:
self.class1.focus = True
def on_enter_text_input1(self):
self.postUser.focus = True
self.postUser.background_color = [0.5, 0.5, 0.5, 1]
def _on_keyboard_down(self, instance, keyboard, keycode, text, modifiers):
if (hasattr(self.postUser, 'focus') and self.postUser.focus) and keycode == 40: # 40 - Enter key pressed
self.postUser.focus = False
self.postUser.background_color = [0, 0, 1, 0.5]
self.insert()
def insert(self):
if self.name.text == "":
self.initial_focusing = True
self.name.focus = True

Python Tkinter: Update entry field from another class

I have a Python Tkinter GUI class as following:
import Tkinter as tk
class AppClient(tk.Frame):
def __init__(self, master):
error_class = ErrorClass()
self.val_error_code = error_class.error_message
tk.Frame.__init__(self, master)
self.pack()
##Frame for error message display
error_frame = tk.Frame(self, highlightbackground="violet", highlightcolor="violet", highlightthickness=1, width=600, height = 60, bd= 0)
error_frame.pack(padx = 10, pady = 5, anchor = 'w')
## error code: Label and Entry
tk.Label(error_frame, text = 'Error message').grid(row = 0, column = 0, sticky = 'w')
self.error_code = tk.Entry(error_frame, background = 'white', width = 27)
self.error_code.grid(row = 0, column = 1, sticky = 'w', padx = 5, pady = 5)
def update_error_messsage(self):
self.error_code.delete(0, 'end')
self.error_code.insert(0, self.val_error_code)
if __name__ == '__main__':
root = tk.Tk()
app_client = AppClient(root)
app_client.mainloop()
I want to update the entry field error_code dynamically by using the function update_error_message. The problem is, error message is continuously updated from another class ErrorClass() and I am receiving the error message to my AppClient class by variable error_message.
How can I update the error_code entry filed whenever the value of the variable error_message got updated in another class (ErrorClass())
The simple solution is that you need a reference to your instance of AppClient inside of ErrorClass, and then allow the instance of ErrorClass to pass the new value to the app
For example:
class AppClient(tk.Frame):
def __init__(self, master):
error_class = ErrorClass(self)
...
def update_error_messsage(self, error_code):
self.error_code.delete(0, 'end')
self.error_code.insert(0, error_code)
class ErrorClass():
def __init__(self, app):
self.app = app
def update_error(self):
...
error_code = 42
self.app.update_error_message(error_code)

Object Oriented Tkinter Coding: Changing text and Updating

So I have been teaching myself Object Oriented Programming for Tkinter projects as I clearly see that they are much more organized for large amounts of coding. However I must admit that I've been coasting by simply copying various bits of coding from online, not fully understanding what its purpose is.
This has lead me to the point that my code does not work at all and I have no idea why not. The first issue is an issue with simply changing an aspect of other widgets.
I have this sample code:
import Tkinter as tk
LARGE_FONT = ("Verdana", 12)
class SeaofBTCapp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
container = tk.Frame(self)
container.pack(side="top", fill = "both", expand = True)
container.grid_rowconfigure(0, weight = 1)
container.grid_columnconfigure(0 , weight = 1)
self.frames = {}
frame = StartPage(container, self)
self.frames[StartPage] = frame
frame.grid(row = 0, column = 0, sticky = "nsew")
self.show_frame(StartPage)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text = "Start Page", font = LARGE_FONT)
label.pack(pady = 10, padx = 10)
button = tk.Button(self, text = "Change Label", command = self.change)
button.pack(pady = 10, padx = 10)
def change(self):
label["text"] = "It has changed"
app = SeaofBTCapp()
app.mainloop()
Which SHOULD be a simple enough code that, with a button press, change the label from "Start Page" to "It has changed". But whenever I run it, it says that the global variable "label" is not defined. Additionally, if I then change it to self.label, it states that StartPage instance has no attribute 'label'. I don't know what I'm doing wrong.
Additionally, in a similar vein, I'm working on a project that has a SideBar class and a Main class tied to one MainApplication class. The Main class takes a value and displays it on a Frame in the Main class. Following this, a button in the SideBar increases that value by 1. But the Main display doesn't update and I have no idea how to tie the Main updating with the button in the SideBar.
import Tkinter as tk
something = [0, 6]
class Main():
def __init__(self, root):
mainboard = tk.Frame(root, height = 100, width = 100)
self.maincanvas = tk.Canvas(mainboard, bd = 1, bg = "white")
mainboard.grid(row = 0, column = 0)
self.maincanvas.grid(row = 0, column = 0)
self.maincanvas.create_text(45, 50, anchor = "center", text = str(something[1]))
class SideBar():
def __init__(self, root):
sidebarframe = tk.Frame(root, height = 100, width = 100)
button = tk.Button(sidebarframe, width = 20, text = "Change Value", command = self.add)
sidebarframe.grid(row = 0, column = 1)
button.grid(row = 0, column = 0)
def add(self):
something[1] += 1
print something[1]
class MainApplication():
def __init__(self, parent):
self.parent = parent
self.sidebar = SideBar(self.parent)
self.main = Main(self.parent)
if __name__ == "__main__":
root = tk.Tk()
MainApplication(root)
root.mainloop()
All help would be appreciated, but please try and not use a lot of technical terms, as I am still learning.
In the first scenario replace:
label = tk.Label(self, text = "Start Page", font = LARGE_FONT)
label.pack(pady = 10, padx = 10)
With:
self.label = tk.Label(self, text = "Start Page", font = LARGE_FONT)
self.label.pack(pady = 10, padx = 10)
And also in the function change put it like this:
self.label["text"] = "It has changed"
And in your second problem i changed the code a little bit so it works:
import Tkinter as tk
something = [0, 6]
class Main():
def __init__(self, root):
mainboard = tk.Frame(root, height = 100, width = 100)
self.maincanvas = tk.Canvas(mainboard, bd = 1, bg = "white")
mainboard.grid(row = 0, column = 0)
self.maincanvas.grid(row = 0, column = 0)
self.maincanvas.create_text(45, 50, anchor = "center", text = str(something[1]))
class SideBar():
def __init__(self, root, main):
self.main = main # Putting the main object to self.main
sidebarframe = tk.Frame(root, height = 100, width = 100)
button = tk.Button(sidebarframe, width = 20, text = "Change Value", command = self.add)
sidebarframe.grid(row = 0, column = 1)
button.grid(row = 0, column = 0)
def add(self):
something[1] += 1
self.main.maincanvas.delete("all") # Removing everything from canvas
self.main.maincanvas.create_text(45, 50, anchor = "center", text = str(something[1])) # Inserting the new value
print something[1]
class MainApplication():
def __init__(self, parent):
self.parent = parent
self.main = Main(self.parent) # The main needs to run first
self.sidebar = SideBar(self.parent, self.main) # So that SideBar can use its canvas
if __name__ == "__main__":
root = tk.Tk()
MainApplication(root)
root.mainloop()

Getting Attribute error in Python

Here is my code along with the error message.
from Tkinter import *
class Window01 (Frame):
def __init__(self, master):
Frame.__init__(self)
self.reveal()
self.create_widget()
self.grid()
def create_widget(self):
self.lbl = Label (self, text = "This is a Widget App.")
self.lbl.grid(row =1, column =0, columnspan =2, sticky = W)
self.entbx = Entry(self)
self.entbx.grid(row = 1, column = 1, sticky = W)
self.bttn = Button (self, text = "Widget Button", command = self.reveal)
self.bttn.grid(row = 2, column = 0, sticky = W)
self.txt = Text (self, width =35, height = 5, wrap = WORD)
self.txt.grid(row = 3, column = 0, columnspan =2, sticky = W)
def reveal (self):
contents = self.entbx.get()
if contents =="magic":
message = "Access Granted"
else:
message = "Denied"
self.txt.delete(0.0, END)
elf.txt.insert(0.0, message)
root = Tk()
root.title ("Widget_Button")
root.geometry ("300x150")
app = Window01 (root)
root.mainloop()
File "C:\PyDev\Py_Widgets101\src\Py_Widget03.py", line 10, in init
self.reveal()
File "C:\PyDev\Py_Widgets101\src\Py_Widget03.py", line 30, in reveal
contents = self.entbx.get()
AttributeError: Window01 instance has no attribute 'entbx'
self.entbx is created by create_widget(). You are calling reveal() -- which requires self.entbx -- before you've called create_widget():
self.reveal()
self.create_widget()