Copyright © 1998-2004 by Jordan Russell
All rights reserved.
Contents
Toolbar97 is a free, open source dockable toolbar component set for Delphi 2.0-7.0 and C++Builder 1.0-6.0 that features the Office 97 look and behavior. Some of its features include:
- Toolbars that can be dragged and docked to any side of a form, or be left floating. Multiple toolbars can lined up side-by-side or in rows.
- All source code for customization if necessary.
- A TToolbarButton97 control that looks and works just like the buttons in Office 97.
- A TEdit97 control for creating Office 97-style edit controls on toolbars.
- Ability to save and load toolbar positions from the registry.
- Full compatibility with Windows 95, 98, 2000, NT 4.0, and 3.51, without requiring the new COMCTL32.DLL, unlike Delphi's TToolBar and TCoolBar components.
- 100% native VCL code -- no bulky OCX's.
Note that the successor to Toolbar97, Toolbar2000, is now available.
Toolbar97 is licensed under the terms of the zlib license.
Toolbar97
Copyright (C) 1998-2004 by Jordan Russell
http://www.jrsoftware.org/
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
IMPORTANT: When unzipping Toolbar97, make sure your unzipper is configured to recreate the directory structure (in WinZip, check Use Folder Names).
Delphi 2.0/C++Builder 1.0 installation:
- Select Component | Install... on the menu bar.
- A dialog entitled Install Components will appear. Click the Add... button.
- On the Add Module dialog, click the Browse button, then locate TB97Reg.pas.
Delphi 2.0/C++Builder 1.0 upgrade:
- Select Component | Rebuild Library on the menu bar.
Delphi 3.0 installation / upgrade:
- Select Tools | Environment Options... on the menu bar. Go to Library tab and add the full path of your Toolbar97 Source directory to the Library Path field if you have not already done so. The Library Path field should then look similar to this:
C:\Delphi3\Lib;C:\Delphi3\Bin;C:\Delphi3\Imports;c:\tb97\source
Click OK.
- Select File | Open... on the menu bar. Set Files of type to Delphi package source, locate and select the TB97_d3 package source file in your Toolbar97 Source directory, and click Open.
- A package editor window will appear. Click Compile, then click Install.
Delphi 4.0 installation / upgrade:
- Select Tools | Environment Options... on the menu bar. Go to Library tab and add the full path of your Toolbar97 Source directory to the Library Path if you have not already done so. The Library Path field should then look similar to this:
$(DELPHI)\Lib;$(DELPHI)\Bin;$(DELPHI)\Imports;c:\tb97\source
Click OK.
- Select File | Open... on the menu bar. Set Files of type to Delphi package source, locate and select the TB97_d4 package source file in your Toolbar97 Source directory, and click Open.
- A package editor window will appear. Click Compile, then click Install.
Delphi 5.0 installation / upgrade:
- Select Tools | Environment Options... on the menu bar. Go to Library tab and add the full path of your Toolbar97 Source directory to the Library Path if you have not already done so. The Library Path field should then look similar to this:
$(DELPHI)\Lib;$(DELPHI)\Bin;$(DELPHI)\Imports;c:\tb97\source
Click OK.
- Select File | Open... on the menu bar. Set Files of type to Delphi package source, locate and select the TB97_d5 package source file in your Toolbar97 Source directory, and click Open.
- A package editor window will appear. Click Compile, then click Install.
Delphi 6.0 installation / upgrade:
- Select Tools | Environment Options... on the menu bar. Go to Library tab and add the full path of your Toolbar97 Source directory to the Library Path if you have not already done so. The Library Path field should then look similar to this:
$(DELPHI)\Lib;$(DELPHI)\Bin;$(DELPHI)\Imports;c:\tb97\source
Click OK.
- Select File | Open... on the menu bar. Set Files of type to Delphi package source, locate and select the TB97_d6 package source file in your Toolbar97 Source directory, and click Open.
- A package editor window will appear. Click Compile, then click Install.
Delphi 7.0 installation / upgrade:
- Select Tools | Environment Options... on the menu bar. Go to Library tab and add the full path of your Toolbar97 Source directory to the Library Path if you have not already done so. The Library Path field should then look similar to this:
$(DELPHI)\Lib;$(DELPHI)\Bin;$(DELPHI)\Imports;c:\tb97\source
Click OK.
- Select File | Open... on the menu bar. Set Files of type to Delphi package source, locate and select the TB97_d6 package source file in your Toolbar97 Source directory, and click Open.
- A package editor window will appear. Click Compile, then click Install.
C++Builder 3.0 installation / upgrade:
- Select Tools | Environment Options... on the menu bar. Go to Library tab and add the full path of your Toolbar97 Source directory to the Library Path field if you have not already done so. The Library Path field should then look similar to this:
$(BCB)\LIB;$(BCB)\LIB\OBJ;c:\tb97\source
Click OK.
- Select File | Open Project... on the menu bar. Locate and select the TB97_cb3 package source file in your Toolbar97 Source directory, and click Open.
- Select Project | Make TB97 on the menu bar.
- Select File | Close All on the menu bar.
- Select Component | Install Packages... on the menu bar. Click the Add... button, locate the file TB97_cb3.bpl in your Toolbar97 Source directory, and click Open.
- Click OK.
C++Builder 4.0 installation / upgrade:
- Select Tools | Environment Options... on the menu bar. Go to Library tab and add the full path of your Toolbar97 Source directory to the Library Path field if you have not already done so. The Library Path field should then look similar to this:
$(BCB)\LIB;$(BCB)\LIB\OBJ;c:\tb97\source
Click OK.
- Select File | Open Project... on the menu bar. Locate and select the TB97_cb4 package source file in your Toolbar97 Source directory, and click Open.
- A package editor window will appear. Click Compile, then click Install.
C++Builder 5.0 installation / upgrade:
- Select Tools | Environment Options... on the menu bar. Go to Library tab and add the full path of your Toolbar97 Source directory to the Library Path field if you have not already done so. The Library Path field should then look similar to this:
$(BCB)\Lib;$(BCB)\Bin;$(BCB)\Imports;$(BCB)\Projects\Bpl;c:\tb97\source
Click OK.
- Select File | Open Project... on the menu bar. Locate and select the TB97_cb5 package source file in your Toolbar97 Source directory, and click Open.
- A package editor window will appear. Click Compile, then click Install.
C++Builder 6.0 installation / upgrade:
- Select Tools | Environment Options... on the menu bar. Go to Library tab and add the full path of your Toolbar97 Source directory to the Library Path field if you have not already done so. The Library Path field should then look similar to this:
$(BCB)\Lib;$(BCB)\Bin;$(BCB)\Imports;$(BCB)\Projects\Bpl;c:\tb97\source
Click OK.
- Select File | Open Project... on the menu bar. Locate and select the TB97_cb6 package source file in your Toolbar97 Source directory, and click Open.
- A package editor window will appear. Click Compile, then click Install.
Once you have Toolbar97 installed, you might want to take a look at the Demo project to see a demonstration of Toolbar97's capabilities.
The Toolbar97 package includes six components: TDock97, TToolbar97, TToolWindow97, TToolbarButton97, TToolbarSep97, and TEdit97. Please read the following sections for important details on each.
TB97.pas
Functions:
- procedure IniLoadToolbarPositions (const Form: TCustomForm; const Filename, SectionNamePrefix: String);
Loads the positions of all toolbars owned by Form to the .INI file specified by Filename. This is provided for backwards compatibility - 32-bit applications should use the registry instead. This should be called when your application starts (usually in the OnCreate handler of your form). If they were not previously saved in the .INI file, IniLoadToolbarPositions has no effect. Each toolbar's data is loaded from a section whose name is the Name property of the toolbar prefixed by SectionNamePrefix.
Example:
procedure TForm1.FormCreate(Sender: TObject);
begin
IniLoadToolbarPositions (Self, 'test.ini', '');
end;
- procedure IniSaveToolbarPositions (const Form: TCustomForm; const Filename, SectionNamePrefix: String);
Saves the positions of all toolbars owned by Form to the .INI file specified by Filename. This is provided for backwards compatibility - 32-bit applications should use the registry instead. This should be called when your application exits (usually in the OnDestroy handler of your form). Each toolbar's data is saved to a section whose name is the Name property of the toolbar prefixed by SectionNamePrefix.
Example:
procedure TForm1.FormDestroy(Sender: TObject);
begin
IniSaveToolbarPositions (Self, 'test.ini', '');
end;
- procedure RegLoadToolbarPositions (const Form: TCustomForm; const BaseRegistryKey: String);
Loads the positions of all toolbars owned by Form from the registry. This should be called when your application starts (usually in the OnCreate handler of your form). If they were not previously saved in the registry, RegLoadToolbarPositions has no effect. BaseRegistryKey is the name of the HKEY_CURRENT_USER subkey it loads the data from. RegLoadToolbarPositions will append the Name of the toolbars onto this. For example, if BaseRegistryKey is Software\My Company\My Program\Toolbars and the Name of a toolbar is MyToolbar, it loads the data from the Software\My Company\My Program\Toolbars\MyToolbar key.
Example:
procedure TForm1.FormCreate(Sender: TObject);
begin
RegLoadToolbarPositions (Self, 'Software\My Company\My Program\Toolbars');
end;
- procedure RegLoadToolbarPositionsEx (const Form: TCustomForm; const RootKey: HKEY; const BaseRegistryKey: String);
Same as RegLoadToolbarPositions, but adds a RootKey parameter for specifying the root key for BaseRegistryKey.
- procedure RegSaveToolbarPositions (const Form: TCustomForm; const BaseRegistryKey: String);
Saves the positions of all toolbars owned by Form to the registry. This should be called when your application exits (usually in the OnDestroy handler of your form). BaseRegistryKey is the name of the HKEY_CURRENT_USER subkey it saves the data to. RegSaveToolbarPositions will append the Name of the toolbars onto this. For example, if BaseRegistryKey is Software\My Company\My Program\Toolbars and the Name of a toolbar is MyToolbar, it saves the data from the Software\My Company\My Program\Toolbars\MyToolbar key.
Example:
procedure TForm1.FormDestroy(Sender: TObject);
begin
RegSaveToolbarPositions (Self, 'Software\My Company\My Program\Toolbars');
end;
- procedure RegSaveToolbarPositionsEx (const Form: TCustomForm; const RootKey: HKEY; const BaseRegistryKey: String);
Same as RegSaveToolbarPositions, but adds a RootKey parameter for specifying the root key for BaseRegistryKey.
TB97Ctls.pas
Variables:
- var ButtonsStayDown: Boolean = True;
When True (the default), TToolbarButton97 controls remain down during the execution of their OnClick handlers, similar to Office 97. When False, they pop up immediately like TSpeedButton.
Description:
Create TDock97 controls at locations you want a TToolbar97 to be able to dock at. These automatically resize as toolbars are docked onto them. Be sure to set the Position property to designate which side of the form the dock is to be located.
Key Properties:
- property AllowDrag: Boolean default True;
When True, toolbars on the dock can be dragged. But when it is False, there are several noteworthy differences: child toolbars are not draggable, the positions of child toolbars are neither loaded nor saved, and toolbars from other docks with AllowDrag set to True cannot be docked to it. Remember you are permitted to create two docks with the same Position, so you could create one dock with AllowDrag set to False and another dock with AllowDrag set to True.
- property Background: TBitmap;
A background bitmap, which is optional. It is tiled across the length of the dock, and any docked toolbars used it also.
- property BackgroundOnToolbars: Boolean default True;
When True, the Background bitmap "shines through" onto docked toolbars.
- property BackgroundTransparent: Boolean default False;
When True, the color of the bottom-left pixel of the Background bitmap is considered transparent, and is replaced by value of the Color property.
- property BoundLines: TDockBoundLines;
TDockBoundLines = set of (blTop, blBottom, blLeft, blRight);
Use this to add extra lines to the sides of the dock. For docks Positioned at the top of the form, it looks best if you set this to [blTop].
- property FixAlign: Boolean default False;
If at run-time you notice a dock not appearing in the location it should, enabling this should correct the problem. This problem only occurs when you have a TDock97 and another control with the same Align setting (i.e. a dock and a list view both set to alLeft). When True, this adds an extra pixel to the width or height so that the VCL is able to align it correctly.
- property LimitToOneRow: Boolean default False;
Set this to True if you want to prevent the user from having more than one row of docked toolbars. I generally don't recommend you enable this (since Office 97 doesn't do this) unless absolutely necessary. If you have fixed-size form that would look wrong with too many rows of toolbars, you should instead respond to the OnResize event of TDock97 to make your form resize itself.
- property Position: TDockPosition;
TDockPosition = (dpTop, dpBottom, dpLeft, dpRight);
Determines where the dock is located on the form.
- property ToolbarCount: Integer;
The number of visible toolbars currently docked.
- property Toolbars[Index: Integer]: TCustomToolWindow97;
Zero-based array of all the visible toolbars that are currently docked.
Events:
- property OnInsertRemoveBar: TInsertRemoveEvent;
TInsertRemoveEvent = procedure(Sender: TObject; Inserting: Boolean; Bar: TToolbar97) of object;
Occurs after a toolbar is docked (Inserting = True) or undocked (Inserting = False).
- property OnRequestDock: TRequestDockEvent;
TRequestDockEvent = procedure(Sender: TObject; Bar: TCustomToolWindow97; var Accept: Boolean) of object;
Occurs whenever a toolbar is moved over the dock as it is being dragged. By setting Accept to False you can prevent a particular toolbar from being docked.
- property OnResize: TNotifyEvent;
Occurs whenever the dock is resized.
Key Methods:
- procedure BeginUpdate;
Disables toolbar arrangement. Call this when moving multiple toolbars on a dock to reduce flicker. Once the changes are complete, call EndUpdate.
- procedure EndUpdate;
Re-enables toolbar arrangement after a call to BeginUpdate.
Description:
This is the toolbar control itself. You can insert TToolbarButton97 controls or any other controls on this. All controls in a TToolbar97 are automatically lined up. To create separators, use the TToolbarSep97 component.
At run-time, any new controls created on a TToolbar97 are initially positioned at the end of the toolbar. To change positions of individual controls at run-time, assign to the OrderIndex property.
Remarks:
When the CloseButton property is True (the default), the toolbar can hide itself if the user clicks the close button on a floating toolbar. Because of this, you should always include an item on a menu that toggles the Visible property so the user can get it back if it is closed. See the demo application source code for an example of this.
Toolbars have to recreate themselves whenever they change from a docked to a floating state and vice versa. Because of this, you may notice that when some types of controls are placed on a toolbar (like TComboBoxes), they don't properly handle this situation (due to bugs) and lose some of their values when docking or undocking occurs. If you notice this happening, you need to respond to the OnRecreating and OnRecreated events. Put code in the OnRecreating event that saves the state of the controls in the toolbar, and put code in the OnRecreated event that restores the state. For example, if you had a TComboBox dropdown list, you could make it save its ItemIndex value into a temporary variable in OnRecreating. OnRecreated could then restore the ItemIndex value from the temporary variable.
Key Properties:
- property ActivateParent: Boolean default True;
Determines whether the parent form is activated when a floating toolbar is clicked.
- property BorderStyle: TBorderStyle default bsSingle;
When set to bsSingle, the toolbar has a 3-D border.
- property Caption;
What appears in the title bar of a floating toolbar.
- property CloseButton: Boolean default True;
When True, a close button appears in the title bar when the toolbar is floating.
- property CloseButtonWhenDocked: Boolean default False;
When True, a close button is displayed when the toolbar is docked.
- property DefaultDock: TDock97;
(Note: The LastDock property is meant to supersede this property.)
The default dock location. This is used when the user double-clicks a floating toolbar. If neither this property nor LastDock are set, nothing will happen.
- property DockableTo: TDockableTo default [dpTop, dpBottom, dpLeft, dpRight];
TDockableTo = set of (dpTop, dpBottom, dpLeft, dpRight);
Specifies which positions the toolbar may be docked at.
- property DockedTo: TDock97;
The TDock97 control that the toolbar is currently docked to. You can assign to this value any TDock97 control that belongs to the same form as the toolbar. To undock a toolbar at design time, delete the value from this property. To undock a toolbar at run time, assign nil to this property.
- property DockMode: TToolWindowDockMode;
TToolWindowDockMode = (dmCanFloat, dmCannotFloat, dmCannotFloatOrChangeDocks);
Determines where the user can drag the toolbar. If this is dmCanFloat, the default, the toolbar can float or dock to any dock matching the criteria set by DockableTo. If this is dmCannotFloat, the toolbar can dock to the same docks but it cannot float. If the user moves the mouse outside a dock, the "Unavailable" mouse cursor is displayed. Also, the toolbar does not respond to double-clicks. If this is dmCannotFloatOrChangeDocks, the user cannot drag the toolbar anywhere outside its current dock.
- property DockPos: Integer;
This is only valid if the toolbar is currently docked (DockedTo <> nil). This is its current horizontal (or vertical, if docked to a left or right dock) position in pixels.
- property DockRow: Integer;
This is only valid if the toolbar is currently docked (DockedTo <> nil). This is the row the toolbar is currently docked at.
- property DragHandleStyle: TDragHandleStyle default dhDouble;
TDragHandleStyle = (dhDouble, dhNone, dhSingle);
Determines the type of drag handle displayed on the left (or top, when docked vertically) of the toolbar.
- property FloatingMode: TToolWindowFloatingMode;
TToolWindowFloatingMode = (fmOnTopOfParentForm, fmOnTopOfAllForms);
By default, this is set to fmOnTopOfParentForm, meaning when floating the toolbar only stays on top of its parent form. If this is set to fmOnTopOfAllForms, the toolbar will stay above all other forms in the project (except those that are also set to stay on top).
- property FullSize: Boolean default False;
When True, the toolbar always fills the entire width (or height, if vertically docked) of the dock, much like Office 97's menu bar.
- property HideWhenInactive: Boolean default True;
When True, the toolbar is hidden whenever the application is deactivated (a characteristic of Office 97's toolbars).
- property LastDock: TDock97;
The TDock97 control that the toolbar was last docked to, or if the toolbar is currently docked, this is equal to the DockedTo property. Double-clicking a floating toolbar will restore it back to the dock specified by this property, and at the same position it previously was docked at. This property overrides and is meant to be a replacement for DefaultDock. If you want to disable the use of this property, set UseLastDock to False.
- property OrderedControls[Index: Integer]: TControl;
Run-time only. OrderedControls works just like the standard Controls property (see Delphi/C++Builder help), except the controls it returns are in the order they appear on the toolbar.
- property OrderIndex[Control: TControl]: Integer;
Run-time only. The OrderIndex property holds the position of each control on the toolbar. Use this property to change the position of a control on a toolbar at run-time. (Note that Toolbar97's loading/saving functions do not save this property.) The first control has an OrderIndex value of zero. Much like the way Delphi's TabOrder property works, assigning to one control's OrderIndex automatically shifts other controls' OrderIndexes back or forward. Following are some examples:
// Move a button to the left of the toolbar
Toolbar971.OrderIndex[ToolbarButton971] := 0;
// Move another button to the right of the toolbar
Toolbar971.OrderIndex[ToolbarButton972] := Maxint;
- property ShowCaption: Boolean default True;
When True, the toolbar displays a caption bar and a close button (if CloseButton is True) when floating.
- property UseLastDock: Boolean default True;
When True, the toolbar saves the last dock it was docked to in the property LastDock, and internally preserves the position it was docked at. See the description of LastDock for more information.
Events:
- property OnClose: TNotifyEvent;
Occurs after the toolbar is hidden in response to the user clicking the toolbar's Close button.
- property OnCloseQuery: TCloseQueryEvent;
TCloseQueryEvent = procedure(Sender: TObject; var CanClose: Boolean) of object;
Same in function as a form's OnCloseQuery event. Setting CanClose to False will cancel the requested close operation.
- property OnDockChanged: TNotifyEvent;
Occurs after the toolbar has changed between docks, or from a docked to floating state or vice versa.
- property OnDockChanging: TNotifyEvent;
Occurs immediately before the toolbar changes between docks, or from a docked to floating state or vice versa.
- property OnDockChangingEx: TDockChangingExEvent;
TDockChangingExEvent = procedure(Sender: TObject; DockingTo: TDock97) of object;
Same as the OnDockChanging event, but has an extra parameter DockingTo that specifies where the toolbar is about to dock to.
- property OnDockChangingHidden: TDockChangingExEvent;
TDockChangingExEvent = procedure(Sender: TObject; DockingTo: TDock97) of object;
Similar to the OnDockChangingEx event, but this event is called after the toolbar has already been hidden from the screen in preparation to be moved to another dock. This can be useful if, for example, you want to make changes in the ordering of the toolbar's controls whenever it moves between docks, but don't want any visible flickering during this time.
- property OnRecreated: TNotifyEvent;
Occurs immediately after the toolbar recreates itself. This usually happens when it changes between a docked and non-docked state. See the Remarks above for more information.
- property OnRecreating: TNotifyEvent;
Occurs immediately before the toolbar recreates itself. This usually happens when it changes between a docked and non-docked state. See the Remarks above for more information.
- property OnResize: TNotifyEvent;
Occurs after the toolbar's size changes. Note that this event is fired after any size change, not only when the user resizes a floating toolbar.
- property OnVisibleChanged: TNotifyEvent;
Occurs after the toolbar's visibility changes. This event will occur if the application manually changes the visibility of the toolbar (e.g., by toggling the Visible property), or if the user closes the toolbar using its Close button.
Key Methods:
- procedure AddDockForm (const Form: TCustomForm);
Adds a form to the list of forms that the toolbar can be docked to besides the current parent's parent form. Keep in mind that moving a toolbar to another form does not change its Owner property; therefore it will still be destroyed when its owner component is destroyed. To change a toolbar's owner, use TComponent's RemoveComponent and InsertComponent methods.
- procedure BeginMoving (const InitX, InitY: Integer);
TToolWindowSizeHandle = (twshLeft, twshRight, twshTop, twshTopLeft, twshTopRight, twshBottom, twshBottomLeft, twshBottomRight);
Forces the toolbar to enter "move" mode. This is called internally whenever the user clicks the drag handle of a docked toolbar or the caption bar of a floating toolbar, and will only work properly if it is called while the left mouse button is still down. The InitX and InitY parameters specify the client coordinates where the mouse button went down at, which determine where the dragging rectangle appears initially. In most cases (0, 0) should be fine.
- procedure BeginSizing (const ASizeHandle: TToolWindowSizeHandle);
TToolWindowSizeHandle = (twshLeft, twshRight, twshTop, twshTopLeft, twshTopRight, twshBottom, twshBottomLeft, twshBottomRight);
Forces the toolbar to enter "size" mode. This is called internally whenever the user clicks one of the resizing handles on the border of a floating toolbar, and will only work properly if it is called while the left mouse button is still down. The ASizeHandle parameter specifies which resizing handle was clicked.
- procedure BeginUpdate;
Disables arrangement of controls on the toolbar. Call this when moving multiple controls on the toolbar to reduce flicker. Once the changes are complete, call EndUpdate.
- procedure EndUpdate;
Re-enables control arrangement after a call to BeginUpdate.
- procedure RemoveDockForm (const Form: TCustomForm);
Removes a form from the list of forms AddDockForm adds to.
- procedure SetSlaveControl (const ATopBottom, ALeftRight: TControl);
Call this when the form is created to designate a top/bottom docked and left/right docked version of a control on the toolbar. At design time, create both versions side-by-side (with no separator in between). Alternatively, you can specify nil for one of the parameters to have no left/right or top/bottom docked version.
See the demo application source code for an example of how to use this.
Description:
This component is very similar to TToolbar97, but has two key differences: contained controls are not arranged automatically, and floating tool windows may be resized freely.
See the help for the TToolbar97 component for explanations of the properties and events not listed here.
Key Properties:
- procedure MinClientHeight: Integer default 32;
The minimum height, in client pixels, that the tool window can be resized to.
- procedure MinClientWidth: Integer default 32;
The minimum width, in client pixels, that the tool window can be resized to.
- procedure Resizable: Boolean default True;
When True, the user may resize the tool window when floating.
Description:
The TToolbarButton97 component is similar to Delphi 3's TSpeedButton component, but it works more like Office 97 and adds some new features. You aren't required to use this for the buttons on toolbar, but it's recommended that you do so.
 Glyphs should be 16x16 pixels (or 17x17 for some disabled glyphs).
See the help for Delphi's TSpeedButton component for explanations of the properties, methods, and events not listed here.
Key Properties:
- property Action;
Delphi 4 only. Specifies an action to associate with the button. The Images property of TToolbarButton97 is not automatically synchronized with the action list's Images property.
- property Alignment: TAlignment default taCenter;
TAlignment = (taLeftJustify, taRightJustify, taCenter);
Specifies the alignment to use when drawing multiple lines of text when WordWrap is set to True. This has no effect on buttons with only a single line of text.
- property CallDormant: Boolean default True;
Run-time only. When True, TToolbarButton97 calls Dormant on the Glyph TBitmap, which greatly reduces GDI resource consumption. The only downside to having CallDormant set to True is that if the bitmap assigned to Glyph was a DDB, it will be converted to a DIB.
- property DisplayMode: TButtonDisplayMode default dmBoth;
TButtonDisplayMode = (dmBoth, dmGlyphOnly, dmTextOnly);
Determines whether the glyph, caption, or both are drawn on the button. You could adjust this if you wanted to, for example, hide glyphs without deleting them.
- property DropdownAlways: Boolean default False;
When True, the button always functions in "dropdown mode," regardless of whether the DropdownMenu property has a menu assigned. This can be useful for implementing your own dropdown functionality using the OnDropdown event.
- property DropdownArrow: Boolean default True;
When True, the button displays an arrow if DropdownMenu is assigned.
- property DropdownArrowWidth: Integer default 9;
Specifies the amount of pixels to reserve for the dropdown arrow. (This doesn't affect the size of the actual arrow.) Typically, this property will only be changed if DropdownCombo is set to True.
- property DropdownCombo: Boolean default False;
When True, the button is split into two parts, like the Undo and Redo buttons in Office 97. Clicking the left side of the button generates an OnClick event, and clicking the right side displays the popup menu specified by DropdownMenu. Note: The extra two pixels on the right side are there intentionally because Office 97 has them, and it provides better separation when you have several DropdownCombo-style buttons in a row.
- property DropdownMenu: TPopupMenu;
When this is assigned, the button will display this popup menu instead of calling the OnClick handler when clicked. The menu appears below the button, or to the right if the parent toolbar is docked to the left or right side.
- property Flat: Boolean default True;
When True, the button has the Office 97 appearance.
- property Glyph: TBitmap;
Same as TSpeedButton's Glyph property, except it supports a fifth glyph which is used when the mouse is over the button.
- property GlyphMask: TBitmap;
This is a special-purpose property. Assigning a bitmap to this property is optional. When one is assigned, TToolbarButton97 uses it as the transparent mask, instead of generating its own based on the value of Glyph's bottom-left pixel. The mask bitmap should contain only black and white colors. This property can be useful when you have a need to display an icon (meaning a TIcon or HICON) as a glyph on a button. Assign its XOR bitmap to Glyph, and its AND bitmap to GlyphMask.
- property HighlightWhenDown: Boolean default True;
When False, the dithered pattern will not be shown when the button's Down property is True.
- property ModalResult: TModalResult default mrNone;
Just like TButton's ModalResult property.
- property ImageIndex: Integer default -1;
The particular image to use from the image list specified by the Images property.
- property Images: TCustomImageList;
This property specifies an optional image list to use for glyphs. When an image list is assigned to this property, it overrides the Glyph/GlyphMask properties.
- property NoBorder: Boolean default False;
When True, the button never displays a border (regardless of the Flat property's setting).
- property NumGlyphs: TNumGlyphs97;
TNumGlyphs97 = 1..5;
Same as TSpeedButton's NumGlyphs property, except it supports a fifth glyph which is used when the mouse is over the button.
- property OldDisabledStyle: Boolean default False;
When True, the generated disabled glyphs look like TSpeedButton and earlier versions of TToolbarButton97. When False, they use the Office 97 look.
- property Opaque: Boolean default True;
When True, the button is not transparent, which prevents the "blinking" effect that you see when you move the mouse over it or click it (as seen in Delphi 3's TSpeedButton, and most other button components). You should only need to set this to False if you have a Background for the dock.
- property Repeating: Boolean default False;
When True, OnClick events are periodically fired while the mouse button is down on the button, using the interval specified by RepeatInterval, and an initial delay specified by RepeatDelay.
- property RepeatDelay: Integer default 400;
The initial delay, in milliseconds, before OnClick events are fired at the rate specified by RepeatInterval. Only applicable when Repeating is True.
- property RepeatInterval: Integer default 100;
The interval, in milliseconds, at which OnClick events are fired after the initial delay specified by RepeatDelay has expired. Only applicable when Repeating is True.
- property ShowBorderWhenInactive: Boolean default False;
When True, button borders are shown even when the application is inactive (applies to flat buttons only).
- property WordWrap: Boolean default False;
When True, text that is too wide for the button is wrapped into several lines.
Key Events:
- property OnDropdown: TButtonDropdownEvent;
TButtonDropdownEvent = procedure(Sender: TObject; var ShowMenu, RemoveClicks: Boolean) of object;
Occurs before the button's dropdown menu is about to be displayed, or if DropdownAlways is True, when the left button is pressed over the button.
If a menu is assigned to the DropdownMenu property, the ShowMenu parameter will be True when the event handler is called. Setting ShowMenu to False will cause it to not show the dropdown menu after the event handler returns.
The RemoveClicks parameter is always initially True. Normally TToolbarButton97 removes from the message queue additional clicks on the button while the dropdown menu is displayed. This is so clicking on the button and clicking it again will close the dropdown menu and not display it again. Setting RemoveClicks to False will prevent this.
- property OnMouseEnter: TNotifyEvent;
Occurs when the mouse cursor moves inside the button.
- property OnMouseExit: TNotifyEvent;
Occurs when the mouse cursor leaves the button.
Description:
The TToolbarSep97 component is used to create separators between toolbar buttons. It automatically adjusts its size and orientation at design and run time. At run time, it also adjusts its visibility when necessary.
Key Properties:
- property Blank: Boolean default False;
When True, the separator does not have a beveled appearance.
- property SizeHorz: Integer default 6;
The width of the separator when the parent toolbar is docked to the top or bottom.
- property SizeVert: Integer default 6;
The height of the separator when the parent toolbar is docked to the left or right.
Description:
The TEdit97 component is identical to Delphi's TEdit component, except it has the Office 97 appearance, and is missing a few properties that are not applicable.
Problem:
I have a combo box on my toolbar, and whenever I dock or undock the toolbar the combo box's ItemIndex property is reset.
Solution:
This is a bug in the VCL (affecting Delphi/C++Builder versions prior to 4.0), not Toolbar97. Please read the Remarks section of the TToolbar97 reference for information on how to work around this.
Problem:
I have my docks at the edges of the form, but for some reason toolbars are docking in unexpected places, such as the middle of the form.
Solution:
This is due to the design of the VCL, and usually happens if you have multiple controls with the same Align setting. Try setting the FixAlign property for the docks exhibiting this problem.
Problem:
My RichEdit component is now limiting itself to 64KB after adding a toolbar to my form.
Solution:
This problem exists in Windows 95 and 98 (not NT/2000). The bug is not in TCustomToolWindow97, but TCustomToolWindow97 does cause this to start happening by its use of a WH_CALLWNDPROC hook. On 95/98, rich-edit controls start misbehaving if a WH_CALLWNDPROC hook of any kind is installed. Unfortunately there is not much that can be done about this problem, since TCustomToolWindow97 needs to use a hook in certain places for it to work properly, and even the latest RICHED32.DLL still has this problem. One option is to use a rich-edit component based on RICHED20.DLL, which does not exhibit this problem, and is Microsoft's current recommendation. (For one such component, try searching for riched98 or richedit98 on the Delphi Super Page. I'm also told that RxLib contains a Rich Edit 2.0 component.)
Problem:
UpDown controls on TToolbar97's conflict with TToolbar97's automatic control arrangement when their Associate property is assigned.
Solution:
Manually synchronize the UpDown control and its buddy window instead of using the Associate property.
- When using the same background on several docks, it is more efficient to assign the background bitmap to one at design time, and copy it to the rest using code at run time. For example:
BottomDock.Background.Assign (TopDock.Background);
LeftDock.Background.Assign (TopDock.Background);
RightDock.Background.Assign (TopDock.Background);
This way you don't wind up with a larger EXE caused by duplicates of the same bitmap.
- To quickly change the DisplayMode of all buttons on a TToolbar97, use code like this:
var
I: Integer;
begin
Toolbar971.BeginUpdate;
try
for I := 0 to Toolbar971.ControlCount-1 do
if Toolbar971.Controls[I] is TToolbarButton97 then
with TToolbarButton97(Toolbar971.Controls[I]) do begin
DisplayMode := ...;
Width := ...;
Height := ...;
end;
finally
Toolbar971.EndUpdate;
end;
end;
- Be warned: The TTreeView components in Delphi versions 2.0-3.02 and C++Builder 1.0 have a memory leak bug. Whenever a toolbar containing a TTreeView is docked or undocked, memory is leaked. More specificially, a TTreeView leaks memory whenever its handle gets "recreated." The amount of memory leaked can range from a few bytes to several thousand bytes, depending on the number of nodes. Delphi 4.0 and C++Builder 3.0 fix this bug.


DB Helper¶
A feature which allows developer to implement and override the database helper methods used by the library.
How is it being used?
When any of the operation of the Repository or DbConnection extended methods has been called, the library is pre-touching the databases to pre-cached all the schemas for performance purposes.
The classes that are mainly used to pre-touched the database is implemented through IDbHelper.
DbHelperMapper¶
This class is used to map the Type of database provider into an instance of IDbHelper object.
By default, the SqlServerDbHelper class is provided by the library which is mainly used for SQL Server DB providers.
A code below is called in the static constructor of this class.
1 2 3 4 5 | static DbHelperMapper()
{
// By default, map the Sql
Add(typeof(SqlConnection), new SqlServerDbHelper());
}
|
If however a custom IDbHelper has been introduced to be a helper method for other databases, let us say MySql, then it can also be mapped using this class.
A code below is a simple call to map a customized IDbHelper class named OracleDbHelper into a MySql DB provider.
1 | DbHelperMapper.Add(typeof(MySqlConnection), new MySqlDbHelper(), true);
|
The last boolean argument is used to override an existing mapping (if present). Otherwise, an exception will be thrown.
IDbHelper¶
An interface used to mark the class to become a database helper. Below is a sample code that implements this interface.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | public class CustomDbHelper : IDbHelper
{
public CustomDbHelper()
{
DbTypeResolver = new SqlDbTypeNameToClientTypeResolver();
}
public IResolver<string, Type> DbTypeResolver { get; }
public IEnumerable<DbField> GetFields(IDbConnection connection, string tableName, IDbTransaction transaction = null)
{
...
}
public Task<IEnumerable<DbField>> GetFieldsAsync(IDbConnection connection, string tableName, IDbTransaction transaction = null)
{
...
}
public object GetScopeIdentity(IDbConnection connection, IDbTransaction transaction = null)
{
...
}
public Task<object> GetScopeIdentityAsync(IDbConnection connection, IDbTransaction transaction = null)
{
...
}
}
|
To map the IDbHelper, simply call the method below.
1 | DbHelperMapper.Map(typeof(SqlConnection), new CustomDbHelper());
|
Once the class CustomDbHelper has been mapped to a specific DB Provider, then the library will automatically use it in all operations for that DB Provider.


Homework 1 - See Lectures
Due Wednesday March 3, 2021 before class
HW1 Submission Sheet
Please create a github repo and provide a public link to the repository in the form above.
Homework 2 - See Lectures
Due Wednesday March 10, 2021 before class
HW2 Submission Sheet
Please create a github repo and provide a public link to the repository in the form above.
Homework 3 - See Lectures
Due Friday March 19, 2021 before class
HW3 Submission Sheet
Please create a github repo and provide a public link to the repository in the form above.
Homework 4 - See Lectures
Due Friday April19, 2021 before class
HW4 Submission Sheet
Please create a github repo and provide a public link to the repository in the form above.


{% extends "base.html" %} {% block content %}
Patients population is located in Piaui, north-eastern Brazil.
FaBRyDB was built using the Harvest framework.
Get Started


Introduction
Pytorch provides two high-level features:
- Tensor computation analogous to numpy but with option of GPU acceleration.
- Deep Neural Networks built on a tape-based autograd system.
And is generally used either as replacement for NumPy (for the GPU acceleration) or as a deep learning research platform.
Components of PyTorch
- torch: Tensor library like NumPy with GPU support.
- torch.autograd: Automatic differentiation library that supports all differentiable Tensor operations.
- torch.nn: neural network library integrated with autograd.
- torch.optim: optimization package used along with torch.nn with standard optimization methods like SGD, RMSProp, LBGFS etc.
- torch.multiprocessing: python multiprocessing, but with memory sharing of Tensors across processes.
- torch.utils: Data loader, trainer and other utility functions.
- torch.legacy(.nn/.optim): legacy code that is ported for backward compatibility.
Tensors
Tensors in torch are analogous to ndarrays in NumPy but differ in that Tensors in torch can be loaded on to GPU for hardware acceleration.
Tensors can be initialized by calling a normal Tensor object or using special purpose functions like torch.rand
. The size
function gives the dimension of the Tensor initialized.
Unlike Tensors in TensorFlow, the ones in PyTorch can be seen after initialization without running a session.
# for python 2.* users
from __future__ import print_function
import torch
x = torch.Tensor(4, 3)
print(x)
x = torch.rand(4, 3)
print(x)
print(x.size())
Tensor Operations
PyTorch gives various options and aliases for operations on tensors as can be seen for addition below
y = torch.rand(4, 3)
# addition using + operator
print(x + y)
# addition using add function
print(torch.add(x, y))
# addition using out parameter of add function
result = torch.Tensor(5, 3)
torch.add(x, y, out=result)
# result is casted for the new dimensions [4*3]
print(result)
# in-place addition
y.add_(x)
print(y)
Standard NumPy-like indexing works on PyTorch Tensors.
print(x[:, 1])
Resizing can be done using torch.view
.
x = torch.randn(4, 4)
y = x.view(16)
# the size -1 is inferred from other dimensions
z = x.view(-1, 8)
print(x.size(), y.size(), z.size())
Some operations available on Tensors are:
- torch.numel: returns the total number of elements in a Tensor.
- torch.eye: returns a 2D tensor representing an identity matrix.
- torch.from_numpy: create a Tensor from NumPy array where the two share the same memory and modifications are reflected across.
- torch.linspace: returns a 1D tensor of equally spaced steps with start and end of a range.
- torch.ones: returns tensor of a defined shape filled with scalar value 1.
- torch.zeros: returns tensor of a defined shape filled with scalar value 0.
- torch.cat: concatenate tensors.
- torch.chunk: splits the tensors into chunks.
CUDA Tensors
- Moving Tensors to CUDA is as simple as calling
.cuda
method. - Calling a simple function,
torch.cuda.is_available
checks if CUDA is available.
The type of a variable moved to GPU differs from the ones not on GPU, and hence addition would lead to TypeError.
if torch.cuda.is_available():
x = x.cuda()
y = y.cuda()
print(x+y)
x = torch.rand(4, 3)
# raises TypeError because of Type Mismatch [torch.FloatTensor, torch.cuda.FloatTensor]
print(x + y)
Autograd: Automatic Differentiation
As the name suggests, the autograd
package provides automatic differentiation for all operations on Tensors. The define-by-run framework ensures that the backprop is defined by how the code is run, and hence every single iteration can be different allowing dynamic modifications between epochs during training which is not possible in other static libraries like TensorFlow, Theano etc. which require a graph compilation by running something like a session and any change in network requires a recompilation of the graph.
Variable
autograd.Variable
is the main class under the autograd package, which wraps a tensor along with almost all of operations defined on it. Upon completion of a process, .backward
method can be called to calculate all the gradients in the backward direction making the back propagation a very minor automatic step in designing the network.
.data
gives the raw tensor in the variable..grad
gives the gradient w.r.t. this variable.
The other important class in autograd package is the Function
class. Variable
and Function
are interconnected to build an acyclic graph, the encodes the complete history of computation.
Every variable has a
.grad_fn
attribute that references theFunction
that has created theVariable
. The variables created by user havegrad_fn
asNone
.
In order to compute derivatives, .backward
function can be called on a variable.
- if
Variable
is a scalar,.backward
does not require any argument. - if
Variable
holds more types of elements, agradient
argument is defined, which is a tensor of a matching shape.
import torch
from torch.autograd import Variable
x = Variable(torch.ones(2, 2), requires_grad=True)
y = x + 2
print(y.grad_fn)
z = y * y * 3
out = z.mean()
print(z, out)
out.backwards()
print(x.grad)
Let the out
variable be \(o\), then,
where \(z_i\) is given by,
So \(o\) in \eqref{1} can be written as,
Differentiating w.r.t. x,
The autograd package can be in general seen as the library that implements these basic differentials and then carefully employs the chain rule to generate gradients for the most complex functions in the program also. This simplifying of the process helps achieve gradients on the fly instead of predefining it for a graph.
Neural Networks
The neural network package, torch.nn
, depends heavily on the autograd
package to define the models and differentiate them.
A basic training process in the neural network involves the following steps:
- define the neural network with learnable parameters (i.e. weights)
- iterate over the training data.
- feed the input through the network.
- compute the loss or other error metrics.
- propagate the gradients back into the network parameters.
- update the weights.
Define a network:
import torch
from torch.autograd import Variable
import torch.nn as nn
import torch.nn.functional as F
# inherit from nn.Module
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
# 1 input image channel, 6 output channels, 5x5 square convolution
# kernel
self.conv1 = nn.Conv2d(1, 6, 5)
self.conv2 = nn.Conv2d(6, 16, 5)
# an affine operation: y = Wx + b
self.fc1 = nn.Linear(16 * 5 * 5, 120)
self.fc2 = nn.Linear(120, 84)
self.fc3 = nn.Linear(84, 10)
def forward(self, x):
# Max pooling over a (2, 2) window
x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))
# If the size is a square you can only specify a single number
x = F.max_pool2d(F.relu(self.conv2(x)), 2)
x = x.view(-1, self.num_flat_features(x))
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x
def num_flat_features(self, x):
size = x.size()[1:] # all dimensions except the batch dimension
num_features = 1
for s in size:
num_features *= s
return num_features
net = Net()
print(net)
Only the forward function needs to be defined because the backward function is already defined using autograd. And the learnable parameters of the network are returned by net.parameters
.
params = list(net.parameters())
print(len(params))
print(params[0].size())
The input to the forward method is autograd.Variable
, and so is the output.
input = Variable(torch.randn(1, 1, 32, 32))
out = net(input)
print(out)
The gradients of all the parameters should be reset to zero before calling the backprops.
net.zero_grad()
out.backward(torch.randn(1, 10))
torch.nn
only supports mini-batches, i.e. input to any nn layer is a 4D Tensor ofsamples * channels * height * width
. If a single sample,input.unsqueeze(0)
adds a fake dimension.
Loss Function
A loss function takes (output, target) pair of inputs, and computes a value that estimates how far away the output is from the target.
output = net(input)
target = Variable(torch.arange(1, 11)) # a dummy target, for example
criterion = nn.MSELoss()
loss = criterion(output, target)
print(loss)
Using the .grad
and .next_functions
one can see the entire graph in the backward direction from the loss. Upon calling the .backward
function the whole graph is differentiated w.r.t. the loss, and all .grad
variables are updated with the accumulated gradients.
Before calling the backward function, existing gradients need to be cleared or they will be accumulated along with the existing gradients, if any.
net.zero_grad()
print('conv1.bias.grad before backward')
print(net.conv1.bias.grad)
loss.backward()
print('conv1.bias.grad after backward')
print(net.conv1.bias.grad)
The simplest way to update the weights is the Stochastic Gradient Descent (SGD).
learning_rate = 0.01
for f in net.parameters():
f.data.sub_(f.grad.data * learning_rate)
Apart from this, PyTorch also allows to use various other algorithms for the purpose of optimizing the network parameters. This is enabled by using the torch.optim
package that implements most of these methods.
import torch.optim as optim
optimizer = optim.SGD(net.parameters(), lr=0.01)
optimizer.zero_grad()
output = net(input)
loss = criterion(output, target)
loss.backward()
optimizer.step()
It can be seen that, even the optimizer package requires the manual reseting of the gradient buffer before backpropagation is invoked, to prevent the accumulation of gradients from different calls.
Handling Data
Data may deal with one of the formats, namely, image, text, audio or video. Standard python packages may be used to load the datasets into the NumPy arrays, which can then easily be converted into the Tensors because of the seamless bridge between the two libraries.
Following packages are recommended:
- Images: Pillow, OpenCV
- Audio: scipy, librosa
- Text: raw Python or Cython based loading, or NLTK and SpaCy
Specifically for vision, the package torchvision is defined, that has loaders for common datasets such as Imagenet, CIFAR10, MNIST etc. and data transformers for images.
REFERENCES:
PyTorch Official Page
PyTorch - Wikipedia
PyTorch - Tensor
Torch (machine learning) - Wikipedia


Hello! I’m Thoa Nguyen, a student currently pursuing a double degree in Informatics and Business Administration at the University of Washington. My understanding of technology and business combined with a focus in human-computer interactions drives my passion for UX/UI design and product management.
Oddly enough, I fell in love with design during a business case competition. Analyzing systems and global market penetration for EY was interesting, but I had so much fun wire-framing and producing a quality prototype. My interests in creating applications lead me to classes like Design Thinking, Client-side Web Development, and Mobile App Design. Outside of those courses, I challenged myself to learn unfamiliar tools like Sketch and participate in local Hackathons.
Connect with me or download my resume below
Managed a development team to create a RESTful web application to help students search through UW course evaluations. Used MySQL to store evaluation records and presented the data in an actionable format through ReactJS. Wire framed a prototype using Figma and displayed the app through Invision.
Used the Google Maps API along with the Trails and Flickr API to enable users to query images of local hiking trails. Built log in system for storing current users and authenticating via Google Firebase. Styled the application using HTML, CSS and Twitter Bootstrap.
Project aimed to improve on existing information technology and gaming platforms in order to help immigrant children learn English effectively (Awarded Most Social Impact - Informatics Project Fair). Designed a website to encapsulate research, games and apps into a single location. Gathered feedback from stakeholders, wrote product specification documents, and created plans for new features and integrations.
Used the Porter's Five Forces and SWOT analysis framework to create growth strategies for Snow Jogger delivery company. Suggested a branding campaign focusing on efficiency, health, and innovative technology. Researched data and developed delivery zone maps for Seattle and Boston.
GPA: 3.46
GPA: 4.0
My design process begins with understanding the target users and end with many iterations. Many of my ideas are outlandish or simply not great, but I continuously revise and make improvements with every draft.


Add new customer
Name:
Tour ID:
Number of adults:
Number of children:
Number of toddlers:
Phone number:
Special requests:
Customer List
Name
Phone Number
Tour ID
Number of Adults
Number of Children
Number of Toddlers
Total Price
Price Paid
Special Requests
Name
Phone Number
Tour ID
Number of Adults
Number of Children
Number of Toddlers
Total Price
Price Paid
Special Requests
Update payment
Price paid:


|
|
-Gladiators- |
doomed |
5,288 |
Humans
|
3,922,352 Gold |
1,641 |
|
|
|
CJET |
2,290 |
Elves
|
585,700 Gold |
1,642 |
|
|
|
kenmoney10 |
1,836 |
Humans
|
1,392,377 Gold |
1,643 |
|
|
La Cosa Nostra |
Maggotdrac |
2,564 |
Elves
|
1,014,414 Gold |
1,644 |
|
|
|
jdunderhil21 |
373 |
Elves
|
1,894,471 Gold |
1,645 |
|
|
|
medicteddy |
5,152 |
Dwarves
|
6,584,688 Gold |
1,646 |
|
|
|
Irishmaori |
191 |
Elves
|
??? Gold |
1,647 |
|
|
Sweet Revenge |
VANISH |
8,348 |
Orcs
|
7,438,638 Gold |
1,648 |
|
|
|
soldier_of_fortune |
70 |
Humans
|
1,117,652 Gold |
1,649 |
|
|
The United Empires |
IRONg0rFz |
3,414 |
Orcs
|
3,288,863 Gold |
1,650 |
|
|
|
AusGamer |
3,210 |
Dwarves
|
4,569,483 Gold |
1,651 |
|
|
|
neetu14 |
2,710 |
Humans
|
2,562,479 Gold |
1,652 |
|
|
|
borntotry |
985 |
Orcs
|
584,073 Gold |
1,653 |
|
|
|
Fishtank-za |
430 |
Dwarves
|
??? Gold |
1,654 |
|
|
|
luis345m |
636 |
Orcs
|
??? Gold |
1,655 |
|
|
|
B3457 |
4,612 |
Humans
|
4,627,844 Gold |
1,656 |
|
|
|
Android |
1,565 |
Dwarves
|
6,607,047 Gold |
1,657 |
|
|
|
AndyRock |
4,064 |
Dwarves
|
1,335,463 Gold |
1,658 |
|
|
Zombie Nation |
Alevont |
16,307 |
Humans
|
10,750,757 Gold |
1,659 |
|
|
|
dankcitykid619 |
742 |
Orcs
|
1,029,819 Gold |
1,660 |
| << Back |
1,877 players total | page 83 of 94 |
Next >> |


Copyright (c) ? Christian Freeling
This game is played on a 7x7 square board with the following setup:
|
| An example [from
here] The Black warrior is attacking the King. Because of that, the King can only move to one of the green dots. The red dots indicate positions that were possible to move if the King was not attacked. The King cannot capture the warrior because they are not adjacent. |
There's also a hexagonal variant HexShakti.


A PERFECT MURDER A film review by Matt Williams
Rating: * * * out of * * * *
Andrew Davis directs this update of Frederick Knott's suspense play, Dial M For Murder, previously made into a masterful Alfred Hitchcock thriller. This time around, the technology and sensibilities have been updated, but the key story remains exciting and suspenseful.
Michael Douglas plays Steven Hayes, a junk bond investor whose world is collapsing. His company is under investigation, his finances are shot, and he recently discovered his wife is having an affair.
Emily (Gwyneth Paltrow), his rich wife, has been frequenting the company of starving artist David Shaw (Viggo Mortensen). Steven develops a carefully constructed plan which could solve all his difficulties. He will hire a man to kill his wife, the he can use her trust fund money to fix his monetary problems.
Fans of the Hitchcock movie, and the original play, will probably already notice some differences. The setup of the movie is roughly the same, but, though a key piece of concluding evidence is the same, the last half of the film is different in all but tone.
Although it is not a stretch, Michael Douglas fits his role perfectly. Though it is nice to see him stretch now and then, and he can play the modern rich white male in his sleep, he does such a good job at it that he's fun just to watch. His character here, as in Dial M for Murder, is a planner. We get to learn his meticulously crafted perfect murder plan, then watch as he rapidly adjusts when things begin to go awry. Michael plays his role in such a way that you hate and cheer for him at the same time.
Gwyneth Paltrow has a beefier part here than Grace Kelly did in Dial M for Murder, and she manages to do an admirable job with it. Though not without sin, she is the most sympathetic of the major three characters. And even if she does make a few big leaps of logic here and there in the film, she maintains credibility throughout.
As the third vertex of the triangle, Viggo Mortensen also has a bigger role than before. Of the three main cast members, he is the weakest, and his motives are never 100% clear. But, he does serve to add a little extra mystery to the plot.
And the plot here is strong (what a welcome change). The additions made to A Perfect Murder enhance the violence of Dial M for Murder, but maintain the same atmosphere.
The end result is about as good as you could hope for in a remake of a Hitchcock classic. It has some rough edges here and there which could have used the master's touch, but, for the most part, A Perfect Murder is an exciting and truly suspenseful thriller.
Copyright 1998 Matt Williams
- Matt Williams<EMAIL_ADDRESS>Reviewer for Cinematter: http://www.cinematter.com Home of over 500 reviews, and information on over 600 upcoming releases
The review above was posted to the
rec.arts.movies.reviews newsgroup (de.rec.film.kritiken for German reviews).
The Internet Movie Database accepts no responsibility for the contents of the
review and has no editorial control. Unless stated otherwise, the copyright
belongs to the author.
Please direct comments/criticisms of the review to relevant newsgroups.
Broken URLs inthe reviews are the responsibility of the author.
The formatting of the review is likely to differ from the original due
to ASCII to HTML conversion.
Related links: index of all rec.arts.movies.reviews reviews


# Copyright (c) 2021 - present / Neuralmagic, Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
Contains code for loggers that help visualize the information from each modifier
"""
import logging
import os
import time
from abc import ABC, abstractmethod
from logging import Logger
from typing import Dict, Union
from numpy import ndarray
from torch import Tensor
try:
try:
from torch.utils.tensorboard import SummaryWriter
except (ModuleNotFoundError, ImportError):
from tensorboardX import SummaryWriter
tensorboard_import_error = None
except Exception as tensorboard_err:
SummaryWriter = None
tensorboard_import_error = tensorboard_err
from sparseml.utils import create_dirs
__all__ = ["PyTorchLogger", "PythonLogger", "TensorBoardLogger"]
[docs]class PyTorchLogger(ABC):
"""
Base class that all modifier loggers must implement.
:param name: name given to the logger, used for identification
"""
def __init__(self, name: str):
self._name = name
@property
def name(self) -> str:
"""
:return: name given to the logger, used for identification
"""
return self._name
[docs] @abstractmethod
def log_hyperparams(self, params: Dict):
"""
:param params: Each key-value pair in the dictionary is the name of the
hyper parameter and it's corresponding value.
"""
raise NotImplementedError()
[docs] @abstractmethod
def log_scalar(
self,
tag: str,
value: float,
step: Union[None, int] = None,
wall_time: Union[None, float] = None,
):
"""
:param tag: identifying tag to log the value with
:param value: value to save
:param step: global step for when the value was taken
:param wall_time: global wall time for when the value was taken
"""
raise NotImplementedError()
[docs] @abstractmethod
def log_scalars(
self,
tag: str,
values: Dict[str, float],
step: Union[None, int] = None,
wall_time: Union[None, float] = None,
):
"""
:param tag: identifying tag to log the values with
:param values: values to save
:param step: global step for when the values were taken
:param wall_time: global wall time for when the values were taken
"""
raise NotImplementedError()
[docs] @abstractmethod
def log_histogram(
self,
tag: str,
values: Union[Tensor, ndarray],
bins: str = "tensorflow",
max_bins: Union[int, None] = None,
step: Union[None, int] = None,
wall_time: Union[None, float] = None,
):
"""
:param tag: identifying tag to log the histogram with
:param values: values to log as a histogram
:param bins: the type of bins to use for grouping the values,
follows tensorboard terminology
:param max_bins: maximum number of bins to use (default None)
:param step: global step for when the values were taken
:param wall_time: global wall time for when the values were taken
"""
raise NotImplementedError()
[docs] @abstractmethod
def log_histogram_raw(
self,
tag: str,
min_val: Union[float, int],
max_val: Union[float, int],
num_vals: int,
sum_vals: Union[float, int],
sum_squares: Union[float, int],
bucket_limits: Union[Tensor, ndarray],
bucket_counts: Union[Tensor, ndarray],
step: Union[None, int] = None,
wall_time: Union[None, float] = None,
):
"""
:param tag: identifying tag to log the histogram with
:param min_val: min value
:param max_val: max value
:param num_vals: number of values
:param sum_vals: sum of all the values
:param sum_squares: sum of the squares of all the values
:param bucket_limits: upper value per bucket
:param bucket_counts: number of values per bucket
:param step: global step for when the values were taken
:param wall_time: global wall time for when the values were taken
"""
raise NotImplementedError()
[docs]class PythonLogger(PyTorchLogger):
"""
Modifier logger that handles printing values into a python logger instance.
:param logger: a logger instance to log to, if None then will create it's own
:param name: name given to the logger, used for identification;
defaults to python
"""
def __init__(self, logger: Logger = None, name: str = "python"):
super().__init__(name)
if logger:
self._logger = logger
else:
self._logger = logging.getLogger(__name__)
def __getattr__(self, item):
return getattr(self._logger, item)
[docs] def log_hyperparams(self, params: Dict):
"""
:param params: Each key-value pair in the dictionary is the name of the
hyper parameter and it's corresponding value.
"""
msg = "{}-HYPERPARAMS:\n".format(self.name) + "\n".join(
" {}: {}".format(key, value) for key, value in params.items()
)
self._logger.info(msg)
[docs] def log_scalar(
self,
tag: str,
value: float,
step: Union[None, int] = None,
wall_time: Union[None, float] = None,
):
"""
:param tag: identifying tag to log the value with
:param value: value to save
:param step: global step for when the value was taken
:param wall_time: global wall time for when the value was taken,
defaults to time.time()
"""
if wall_time is None:
wall_time = time.time()
msg = "{}-SCALAR {} [{} - {}]: {}".format(
self.name, tag, step, wall_time, value
)
self._logger.info(msg)
[docs] def log_scalars(
self,
tag: str,
values: Dict[str, float],
step: Union[None, int] = None,
wall_time: Union[None, float] = None,
):
"""
:param tag: identifying tag to log the values with
:param values: values to save
:param step: global step for when the values were taken
:param wall_time: global wall time for when the values were taken,
defaults to time.time()
"""
if wall_time is None:
wall_time = time.time()
msg = "{}-SCALARS {} [{} - {}]:\n".format(
self.name, tag, step, wall_time
) + "\n".join("{}: {}".format(key, value) for key, value in values.items())
self._logger.info(msg)
[docs] def log_histogram(
self,
tag: str,
values: Union[Tensor, ndarray],
bins: str = "tensorflow",
max_bins: Union[int, None] = None,
step: Union[None, int] = None,
wall_time: Union[None, float] = None,
):
"""
:param tag: identifying tag to log the histogram with
:param values: values to log as a histogram
:param bins: the type of bins to use for grouping the values,
follows tensorboard terminology
:param max_bins: maximum number of bins to use (default None)
:param step: global step for when the values were taken
:param wall_time: global wall time for when the values were taken,
defaults to time.time()
"""
if wall_time is None:
wall_time = time.time()
msg = "{}-HISTOGRAM {} [{} - {}]: cannot log".format(
self.name, tag, step, wall_time
)
self._logger.info(msg)
[docs] def log_histogram_raw(
self,
tag: str,
min_val: Union[float, int],
max_val: Union[float, int],
num_vals: int,
sum_vals: Union[float, int],
sum_squares: Union[float, int],
bucket_limits: Union[Tensor, ndarray],
bucket_counts: Union[Tensor, ndarray],
step: Union[None, int] = None,
wall_time: Union[None, float] = None,
):
"""
:param tag: identifying tag to log the histogram with
:param min_val: min value
:param max_val: max value
:param num_vals: number of values
:param sum_vals: sum of all the values
:param sum_squares: sum of the squares of all the values
:param bucket_limits: upper value per bucket
:param bucket_counts: number of values per bucket
:param step: global step for when the values were taken
:param wall_time: global wall time for when the values were taken,
defaults to time.time()
"""
if wall_time is None:
wall_time = time.time()
msg = "{}-HISTOGRAM {} [{} - {}]: cannot log".format(
self.name, tag, step, wall_time
)
self._logger.info(msg)
[docs]class TensorBoardLogger(PyTorchLogger):
"""
Modifier logger that handles outputting values into a TensorBoard log directory
for viewing in TensorBoard.
:param log_path: the path to create a SummaryWriter at. writer must be None
to use if not supplied (and writer is None),
will create a TensorBoard dir in cwd
:param writer: the writer to log results to,
if none is given creates a new one at the log_path
:param name: name given to the logger, used for identification;
defaults to tensorboard
"""
def __init__(
self,
log_path: str = None,
writer: SummaryWriter = None,
name: str = "tensorboard",
):
super().__init__(name)
if tensorboard_import_error:
raise tensorboard_import_error
if writer and log_path:
raise ValueError(
(
"log_path given:{} and writer object passed in, "
"to create a writer at the log path set writer=None"
).format(log_path)
)
elif not writer and not log_path:
log_path = os.path.join(".", "tensorboard")
if log_path:
create_dirs(log_path)
self._writer = writer if writer is not None else SummaryWriter(log_path)
[docs] def log_hyperparams(self, params: Dict):
"""
:param params: Each key-value pair in the dictionary is the name of the
hyper parameter and it's corresponding value.
"""
try:
self._writer.add_hparams(params, {})
except Exception:
# fall back incase add_hparams isn't available, log as scalars
for name, val in params.items():
self.log_scalar(name, val)
[docs] def log_scalar(
self,
tag: str,
value: float,
step: Union[None, int] = None,
wall_time: Union[None, float] = None,
):
"""
:param tag: identifying tag to log the value with
:param value: value to save
:param step: global step for when the value was taken
:param wall_time: global wall time for when the value was taken,
defaults to time.time()
"""
self._writer.add_scalar(tag, value, step, wall_time)
[docs] def log_scalars(
self,
tag: str,
values: Dict[str, float],
step: Union[None, int] = None,
wall_time: Union[None, float] = None,
):
"""
:param tag: identifying tag to log the values with
:param values: values to save
:param step: global step for when the values were taken
:param wall_time: global wall time for when the values were taken,
defaults to time.time()
"""
self._writer.add_scalars(tag, values, step, wall_time)
[docs] def log_histogram(
self,
tag: str,
values: Union[Tensor, ndarray],
bins: str = "tensorflow",
max_bins: Union[int, None] = None,
step: Union[None, int] = None,
wall_time: Union[None, float] = None,
):
"""
:param tag: identifying tag to log the histogram with
:param values: values to log as a histogram
:param bins: the type of bins to use for grouping the values,
follows tensorboard terminology
:param max_bins: maximum number of bins to use (default None)
:param step: global step for when the values were taken
:param wall_time: global wall time for when the values were taken,
defaults to time.time()
"""
self._writer.add_histogram(tag, values, step, bins, wall_time, max_bins)
[docs] def log_histogram_raw(
self,
tag: str,
min_val: Union[float, int],
max_val: Union[float, int],
num_vals: int,
sum_vals: Union[float, int],
sum_squares: Union[float, int],
bucket_limits: Union[Tensor, ndarray],
bucket_counts: Union[Tensor, ndarray],
step: Union[None, int] = None,
wall_time: Union[None, float] = None,
):
"""
:param tag: identifying tag to log the histogram with
:param min_val: min value
:param max_val: max value
:param num_vals: number of values
:param sum_vals: sum of all the values
:param sum_squares: sum of the squares of all the values
:param bucket_limits: upper value per bucket
:param bucket_counts: number of values per bucket
:param step: global step for when the values were taken
:param wall_time: global wall time for when the values were taken,
defaults to time.time()
"""
self._writer.add_histogram_raw(
tag,
min_val,
max_val,
num_vals,
sum_vals,
sum_squares,
bucket_limits,
bucket_counts,
step,
wall_time,
)


RATING: 9/10 - Alternative rating: B
After the incredible and unexpected success of "Shakespeare in Love", the audience has again opened their eyes for Shakespeare. Michael Hoffman is one of many that dived into the ocean of Shakespeare's poetry during the last decade.
"A Midsummer Night's Dream" has been adapted to the big screen several times, with no particular luck. It can be a dark nightmare, a slapstick comedy, or a semi-serious melodrama, depending on the script adaptation. Hoffman has chosen the most common and easiest way - light comedy. The director creates an enchanting atmosphere, as he brings to the foreground of the story beauty and elegance - a magical recipe for the recreation of a timeless story that is as relevant today as it was when Shakespeare wrote it.
Hoffman didn't move too far away from Shakespeare's original play: Egeus, father to Hermia, has betrothed his daughter to Demetrius, a man who in turn loves her with all his heart, and is determined not to let Hermia's true love, Lysander, from inheriting his bride. Then Hermia and Lysander plot to run away together to avoid this marriage, and even possible death. They share their plot with Helena, a close friend, who unfolds on-stage her own attraction to Demetrius. She then storms out to inform him of what Hermia and Lysander are doing, which will benefit Helena later, as she suspects that her decision to reveal this plan to Demetrius will show him how much she loves him. The parallel story unfolds in the forest nearby, where magic transforms the grounds into an enchanted parallel world. Here we meet the fairies. Oberon (Rupert Everett) and Titania (Michele Pfeiffer ) are king and queen of this charming race, but that does not prevent them from feuding over an Indian boy Titania cares for.  Continuing with this plan, the king hears the accounts of the four lovers as they become separated from each other in the forest. He furthers his orders to Puck, who is instructed to apply this nectar to Demetrius, so that he can fall in love with Helena, and all will be solved. But the plan is distracted when Puck gets the men confused, and applies the nectar to Lysander's eyes. He then exits the scene, and Lysander, as you probably know, falls in love with Helena at first sight. Before the dawn all of Puck's mistakes have been corrected, the magic is gone, the madness ends and the right lovers fall in love with the people they are supposed to.
With his 1996 production of "Hamlet", Kenneth Branagh showed that it is possible to conceive a brilliant adaptation of the full text, but the result will have a long running time, which would be unfortunate for a film like "A Midsummer Night's Dream". Hoffman has chosen a more traditional approach of snipping speeches by removing "extraneous" material. He is less successful. Because of the minor problems in the script and unusually rare and difficult language the acting feels at times cold and removed, as if the actors themselves didn't quite understand the lines they are pronouncing. Christian Bale is pale, Calista Flockheart and Anna Friel are acceptable and Dominic West is at times interesting. But it is only Kevin Cline, Stanley Tucci and Michelle Pfieffer that feel confortable in their roles and really give extraordinary performances. Hoffman's magical picture relies more on performances and atmosphere than on special effects and depth of story. There are no computerized creatures and characters, which is a nice break from Lucas and Besson. The technically simple, but incredibly effective art/set direction dazzle the viewer with golden sparks and fairies, shining in the air like gems, and silver moonlight shining upon our lovers. Makeup and costume design are spectacular in creating a variety of odd denizens, including a rather frightening- looki ng medusa and a few alien-looking creatures. The movie captures the delicate beauty of this Shakespearean comedy. In a magical fantasy setting like this, lines as "The course of true love never did run smooth" and "Lord, what fools these mortals be" work exceptionally well.
The director changed setting from 16th century Greece to Tuscany in the late 1800s. The bicycle – a relatively new invention at the time – becomes a key prop, allowing characters to pedal after each other instead of chasing around on foot. It also provides one of the movie's most amusing sight gags. But he is careful, this is not "Romeo + Julia". The modernizations aren't too modern, and his changes to the original story are minor and almost unnoticeable.
We have had many spectacular adaptations of Shakespeare's undying masterpieces. And it is a tuff task to compare with them. Michael Hoffman's new feature doesn't capture the depth and grandness of Kenneth Brannagh's "Hamlet", nor does it possess the power and originality of Richard Loncrane's "Richard III" . But its magical beauty and appealing plot make it good enough to stand in such exceptional company.
Written by Alex Ioshpe (C) 1999
The review above was posted to the
rec.arts.movies.reviews newsgroup (de.rec.film.kritiken for German reviews).
The Internet Movie Database accepts no responsibility for the contents of the
review and has no editorial control. Unless stated otherwise, the copyright
belongs to the author.
Please direct comments/criticisms of the review to relevant newsgroups.
Broken URLs inthe reviews are the responsibility of the author.
The formatting of the review is likely to differ from the original due
to ASCII to HTML conversion.
Related links: index of all rec.arts.movies.reviews reviews


After finishing the Codeacademy course for Python, I made a text-based "Rock, Paper, Scissors" game limited to the console.
So just for fun, I decided to incorporate an interactive version of that game here. To play, type and submit one of the following letters to battle the computer:
r (rock), p (paper), or s (scissors).


{% blocktrans with name=context.name%}Are you sure you want to delete the context {{name}}?{% endblocktrans %}
{% trans "All attempt data accessed through this context will be deleted." %}
{% extends "numbas_lti/management/admin/base.html" %} {% load i18n %} {% load bootstrap %} {% block title %}{% blocktrans with name=context.name %}Delete "{{name}}"{% endblocktrans %} - {{block.super}}{% endblock %} {% block management_content %}
{% blocktrans with name=context.name%}Are you sure you want to delete the context {{name}}?{% endblocktrans %}
{% trans "All attempt data accessed through this context will be deleted." %}


Kevin is a freshman at University of California, Santa Cruz, pursuing a Bachelors of Science in Computer Science and a minor in Technology and Information Management.
Personal Website
LinkedIn
GitHub


Educational Application
Overview
Android application
Team of 2
Designed and implemented for Project Design course (Paris Sud University - 2018)
Objectives
When we were kids, we used to play outside all day and do all kinds of social and physical games with
friends. We used to use our hands and make crafts, touch the soil in the garden, and have a fun time helping
mom set the table. We did not need expensive toys to feel like we are queens and kings of our fortress
made out of pillows.
But things have changed. Technology’s impact on our daily lives cannot be denied. Kids nowadays learn
how to use an iPad at an early age. Although an iPad could be used as an amazing educational tool, it can
keep the child from developing other necessary skills like sensory-motor skills and social interactions. As
parents and tutors, it is our responsibility to control the balance, rather than using digital devices as
babysitters.
Statistics show that physical, psychological, and behavior disorders are increased due to the overuse of
technology in developing children. Interfering with sleep and schoolwork, cutting into family time, causing
less physical activities, and causing short attention span are among common consequences of using
technology. But there can be more severe outcomes such as autism, developmental delays, obesity, anxiety, or depression.
As Human-Computer Interaction researchers and designers it should be one of our main and urgent
concerns to help parents overcome this issue without the need to completely separate the child from the digital
world.
Brainstorm
We generated around 40 ideas, from which we filtered out 9. We ranked ideas regarding
We kept our top 3 ideas and moved on to the next steps.
Interviews
We conducted several interviews with parents and their children separately and asked them about their life routines. We learned that kids' desire to use digital devices is strongly linked with the amount of time that parents spend with them. Kids are very willing to drop their digital devices when they have a chance to spend time with their parents. Also, if parents use their phones and computers all the time, this is what the kids also want to do.
Design Concept
"A shared space between the child and the parent to do activities together."
We named it Kiddo.
Kiddo is an application that gives kids and their parents' plenty of ideas of what to do together that are not just fun,
but also educational. To put it in other words: Kiddo is an answer when the kid says "I'm
bored" or when the parent wants to spend some time with the kid. The activities are divided into categories
such as cooking, crafts, games, and science, and all these activities have something in common - they
are all a bridge between the digital and the real world. For example, when the parents want to have 30
minutes of quality time with their child, they go to the app and select an activity to do together, like
making a simple meal following the cooking instructions, singing a karaoke song together, camping in
the living room, or making shopping list.
It also provides daily challenges to make the kid connected to at least one physical activity each day.
Daily activities can be really simple like making origami, yet they are the rewarding system that helps us engage the kid.
Risks
The greatest risk that we took was something we found out from the interviews: there are two types of parents: one enjoys spending hours searching the web for activities, and the other one doesn't want to spend time searching the web nor wants to decide which activity is the best. Kiddo is only targeting the second group.
Conclusion
The problem that we tried to tackle affects all parents around the world and it’s growing as the digital world is expanding. Parents would love to have a stronger bond with their kids and have them improve their motor and social skills. As the first generation that has been surrounded by digital devices from birth hasn’t fully grown up yet, we don’t know the consequences that this digital world can have on their futures. We at Kiddo were trying to give our best to alleviate the side effects that all the technology might have on children.
Desktop Game
Overview
Desktop game implemented in Java Swing (Not a wise choice for 2018, but that wasn't my decision to make.)
Solo project
Designed and implemented for Programming of Interactive Systems course (Paris Sud University - 2018)
Objectives
The objective was to design a game with the same concept as the old Minesweeper game (Meaning that there should be bombs hidden in cells and the goal is to uncover all the safe cells), but with a fresh user experience.
Storyboard
I introduced "lock" and "rotation" features and demonstrated their functionality in a storyboard.
Lock Feature
Locking a cell would defuse the bomb beneath the cell, and the numbers on the adjacent cells would update as a result. Users can reuse the lock feature after a certain amount of turns. All the other functionalities are disabled while using the lock.
Rotation Feature
Users can rotate adjacent cells around one arbitrary cell. It changes the location of bombs (if there are any), so the numbers will change. Of course, the user only can see the difference in already uncovered cells. The degree of rotation depends on the speed and strength of user input. The total number of rotations is limited in a game.
Implementation
Here is a screenshot of the final version in which I replaced the numeric system (to show the number of neighbor cells containing bombs) with a color-coding system.
Digital Fabrication Project
Overview
Wooden Marionette controlled by an Android app
Using an Arduino board, Step Motors and Bluetooth module
Solo project
Designed and implemented for Digital Fabrication course (Paris Sud University - 2018)
Design Concept
Social media are subconsciously affecting our way of thinking and behavior. Instagram, for instance, is defining beauty and happiness in the shapes of flawless skinny women and colorful healthy food having zero calories. This mannequin is a symbol of us all, blindly following meaningless trends.
Materials and Hardware
I designed the puppet and the structure to hold the hardware and threads using photoshop, and I used a laser cutter machine
to cut all the parts out of a 3mm MDF.
An Arduino Uno board was used to control two step-motors, each connected to one hand of the puppet.
I used a Bluetooth module to connect the board to the Android app.
Software
I developed an Android app with a UI similar to Instagram's Explore tab, with pictures of women posing for the camera. By pressing the "Follow" button above each picture, the puppet's hands would move to imitate the position of the woman's hands in the shown picture.
Augmented Reality Game
Overview
Augmented Reality Game, developed using Vuforia framework and Unity
Solo project
Designed and implemented for Mixed Reality and Tangible Interaction course (Paris Sud University - 2018)
Design Concept
Alchemy is an AR game based on the idea of so many popular games, such as the Alchemy android application. In this game, the player starts with the four elements of matter: Air, Water, Fire, and Earth, and creates new elements/objects by combining already existing ones.
The Game
I developed this game using the Vuforia framework, the most widely used platform for AR development, with support for most phones, tablets, and eyewear.
To play this game, you would need a device, a camera (an integrated webcam also works), and a set of physical markers. You start the game with four markers (each corresponding to one basic element of matter) and a 3D landscape picture on the device's screen. The landscape is semi-transparent at first, but as you progress in the game and find new elements, they become opaque and fill up the landscape. The goal is to create all the elements by trying out different combinations and completing your 3D scene.
As an interaction design practice, I tried to make the "element creation" as intuitive as possible. This was made possible using the proximity of the markers in a 3D space: E.g. if you take the markers for fire and water and bring them close enough, you would create the steam element. This new element then needs to be registered on a new marker with a unique QR code to be used in further combinations.
There are 40 unregistered markers at the start of the game and they have a virtual question mark 3D model attached. Once you register a newly created element to a marker (by simply bringing it close to the element), the question mark gets replaced by the new element's 3D model.
Technically speaking, the most challenging part was to replace a marker's 3D model in real-time and with the right dimensions and properties.
Models
I made 3D low-poly models of elements and the landscape using Blender. It was my first attempt at 3D visual design and was very fun.
Social Network Application
Overview
Iranian Social Network Android Application
Done by a team of 6
Product Owner: Rahnema Co. Tehran, Iran - 2015
The Story
Jomlak was already on the market and had around 1 million active users when I joined the team.
The company had decided to redesign the client and upgrade server technology as well.
Me and 2 other team members started to build an Android app from the scratch. I was mostly focused on user interactions and UX.
I also was responsible for firts-time users onboarding.
Later on, when we were halfway through updating the client, we started to program a RESTful Java API for the backend.
We finished both client and server in a period of 6 months.
The company decided to shut down Jomlak in 2017.
Framework Modules
Overview
Pervasive Game Modules
Solo project
Designed and implemented for my Bachelor thesis (HCI Lab, Sharif University of Technology, Tehran, Iran - 2016)
Design Concept
A pervasive game is a game that goes beyond the bounds of one screen.  We at the HCI lab were designing a generic framework for pervasive games that could be used for many different applications and scenarios.
Modules
Framework Architecture
A simplified version of the framework's architecture is shown in the image below. Modules that I had to develop were basically used in either user interaction or user behavior recognition.
Illustration Collection
The Story
I enjoy making illustrations even though I have never learned the required techniques properly. I made an Instagram page as an obligation to publish works more frequently.
Game Application
Overview
Android driving game
Done by a team of 2
2014
The Story
Salar was a driving game developed for Android devices. Its special feature was the use of nostalgic items like vintage cars and old songs. We were a team of 2 and I was the graphic designer. Design and development of Salar took around 4 months and it was a success in Iran's market.
Educational Application
Overview
Children's Android application for learning Persian language
Team of 3
2014
The Story
At the beginning of summer 2014, I
was accepted into a summer school run by Rahnema Co, a company that supports startups.
I formed a team and we took all the steps from designing and developing a mobile application
to releasing it on the market. We won the summer school’s best project award, and
our application was a success on the market. It was
installed on more than 50,000 devices.
We were two developers and a graphic designer. We never talked about selecting a leader, but as we were moving forward on our project, I took the responsibility. I was the one who managed the tasks, analyzed the results, and planned the next steps. I also started to be the link between the group and the mentors.
That experience enhanced my different skills. It was the first time that I collaborated with
non-technical students like graphic designers and musicians in a team. I
also learned that when we find out that we are in the wrong direction we should take some
steps back and fix the problem no matter how painful it is.
We had three hard deadlines during the summer. And some of the teams were excluded
at each deadline. At the end of the summer, our product was far better than those of the
other teams and we earned the chance to work on it for another month to prepare it to be
released on market.
After publishing the application, the company offered me two options. First, to get hired
in the main office of the company and work on current projects. Second, to run my
startup under their support according to my plans for our product. Unfortunately,
I had to select the first one. Because I was still a junior student and running a startup needed too
much time and effort, and also because I was not experienced enough. So I decided to work
in the company on larger projects to increase my knowledge and improve my skills.
Card Game
Overview
Boyfriend's Birthday Gift
Solo project
2021
The Game
The rules of play in this game are the same as in Exploding Kittens, and the illustrations are inspired by our relationship.
Rehabilitation Exergame
Overview
Rehabilitation Exergame for Physically Impaired Children
Solo Project
Master Thesis (Technical University of Berlin - 2020 / 2021)
Overview
Children with physical impairments can benefit from easily accessible and engaging motor therapeutic tools in conjunction with their regular rehabilitation programs. Interactive rehabilitation games show promise in aiding physical impairments, as they could increase active participation in therapy while being less costly and requiring less effort in comparison to therapy sessions. In this project, I explored the design space for rehabilitation games and followed an iterative user-centered design approach to offer a solution in the form of an exergame. I developed the designed game and conducted subjective and objective user studies to evaluate the engagement of the game. The results showed a high engagement rate, as well as a significant difference in player engagement between variations of the game, where different types of challenges, namely motoric and cognitive, were included.
Game Concept
As a first step towards the concepting phase, a set of possible game mechanics and game genres
for our exergame, as well as the physiotherapy exercises that should be developed into
interactions with the game, were defined. After that, we did a brainstorming session focused on the
interactions, followed by a revising session to adapt the raw ideas to one or more of the
game mechanics/genres. Three refined ideas made it to the concepting phase, and after an extensive feedback round and an expert interview, one idea was chosen as the game concept: "Pop" the balloons.
This idea was simple yet very powerful in terms of applicable motoric and cognitive variations.
System Architecture
the flow of interaction starts with the player’s input which is their webcam video stream. The webcam captures the player’s hand/arm movements and passes it on to the image recognition module. The image recognition module then will process the stream as a sequence of images and will recognize hand-shaped objects and track their position in each image and then forward this data in text format to the game’s logic module. The game logic reacts to the data received from the image recognition module and provides visual and audio feedback to the player if necessary. If the received data triggers any kind of feedback it will be considered as an ”event”. The array of the events are saved at the end of each game session as a reference for game statistics and player’s performance. In parallel with this primary flow, there is a secondary flow of data which results in having the player’s webcam stream as the game background in the game’s interface.
To realize the functionality of each component and the communication amongst them, making use of several technologies and frameworks was unavoidable. In most cases, wiring several different frameworks harm the overall performance, yet, the final and presented setting outperformed several alternative solutions in which fewer frameworks came into play.
Experiment Design
An experiment was designed to measure the impact of challenge type on various engagement indicators, both subjectively and objectively. It was also interesting to explore interaction patterns qualitatively and extract research questions for further investigations. For subjective evaluation, a questionnaire was used, and the testing sessions were video recorded to be analyzed objectively after the test.
Results
Multiple variations of this exergame featuring different kinds of challenges were compared with each other, both subjectively and objectively. The results of these comparisons suggest that between motoric and cognitive challenges, motoric challenges positively impact the flow experience. The game, as a whole, showed promise in terms of engagement, and I believe that we will see more of similar attempts in rehabilitation games in the future.
Entertainment Application
Overview
Entertainment android app for sending virtual postcards to loved ones
Done by a team of 6
Product owner: Rahnema Co. Tehran, Iran - 2016
The Story
After my first working experience in Rahnema Co, I decided to focus on my studies for a while.
In a short period of two months between two semesters I had some time to refresh my mood by working on a new project at Rahnema.
We were three developers and I was one of the two android developers.